可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I want to print out the contents of a vector in C++, here is what I have:
#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
#include <vector>
#include <sstream>
#include <cstdio>
using namespace std;
int main()
{
ifstream file(\"maze.txt\");
if (file) {
vector<char> vec(istreambuf_iterator<char>(file), (istreambuf_iterator<char>()));
vector<char> path;
int x = 17;
char entrance = vec.at(16);
char firstsquare = vec.at(x);
if (entrance == \'S\') {
path.push_back(entrance);
}
for (x = 17; isalpha(firstsquare); x++) {
path.push_back(firstsquare);
}
for (int i = 0; i < path.size(); i++) {
cout << path[i] << \" \";
}
cout << endl;
return 0;
}
}
How do I print the contents of the vector to the screen?
回答1:
Purely to answer your question, you can use an iterator:
std::vector<char> path;
// ...
for (std::vector<char>::const_iterator i = path.begin(); i != path.end(); ++i)
std::cout << *i << \' \';
If you want to modify the vector\'s contents in the for loop, then use iterator
rather than const_iterator
.
But there\'s lots more that can be said about this. If you just want an answer you can use, then you can stop here; otherwise, read on.
auto (C++11)/typedef
This is not another solution, but a supplement to the above iterator
solution. If you are using the C++11 standard (or later), then you can use the auto
keyword to help the readability:
for (auto i = path.begin(); i != path.end(); ++i)
std::cout << *i << \' \';
But the type of i
will be non-const (i.e., the compiler will use std::vector<char>::iterator
as the type of i
).
In this case, you might as well just use a typedef
(not restricted to C++11, and very useful to use anyway):
typedef std::vector<char> Path;
Path path;
// ...
for (Path::const_iterator i = path.begin(); i != path.end(); ++i)
std::cout << *i << \' \';
counter
You can, of course, use a integer type to record your position in the for
loop:
for(int i=0; i<path.size(); ++i)
std::cout << path[i] << \' \';
If you are going to do this, it\'s better to use the container\'s member types, if they are available and appropriate. std::vector
has a member type called size_type
for this job: it is the type returned by the size
method.
// Path typedef\'d to std::vector<char>
for( Path::size_type i=0; i<path.size(); ++i)
std::cout << path[i] << \' \';
Why not just use this over the iterator
solution? For simple cases you might as well, but the point is that the iterator
class is an object designed to do this job for more complicated objects where this solution is not going to be ideal.
range-based for loop (C++11)
See Jefffrey\'s solution. In C++11 (and later) you can use the new range-based for
loop, which looks like this:
for (auto i: path)
std::cout << i << \' \';
Since path
is a vector of items (explicitly std::vector<char>
), the object i
is of type of the item of the vector (i.e., explicitly, it is of type char
). The object i
has a value that is a copy of the actual item in the path
object. Thus, all changes to i
in the loop are not preserved in path
itself. Additionally, if you would like to enforce the fact that you don\'t want to be able to change the copied value of i
in the loop, you can force the type of i
to be const char
like this:
for (const auto i: path)
std::cout << i << \' \';
If you would like to modify the items in path
, you can use a reference:
for (auto& i: path)
std::cout << i << \' \';
and even if you don\'t want to modify path
, if the copying of objects is expensive you should use a const reference instead of copying by value:
for (const auto& i: path)
std::cout << i << \' \';
std::copy
See Joshua\'s answer. You can use the STL algorithm std::copy
to copy the vector contents onto the output stream. This is an elegant solution if you are comfortable with it (and besides, it is very useful, not just in this case of printing the contents of a vector).
std::for_each
See Max\'s solution. Using std::for_each
is overkill for this simple scenario, but it is a very useful solution if you wanted to do more than just printing to screen: using std::for_each
allows you to do any (sensible) operation on the vector contents.
overload ostream::operator<<
See Chris\'s answer, this is more a complement to the other answers since you will still need to implement one of the solutions above in the overloading. In his example he used a counter in a for
loop. For example, this is how you could quickly use Joshua\'s solution:
template <typename T>
std::ostream& operator<< (std::ostream& out, const std::vector<T>& v) {
if ( !v.empty() ) {
out << \'[\';
std::copy (v.begin(), v.end(), std::ostream_iterator<T>(out, \", \"));
out << \"\\b\\b]\";
}
return out;
}
Usage of any of the other solutions should be straightforward.
conclusion
Any of the solutions presented here will work. It\'s up to you and the code on which one is the \"best\". Anything more detailed than this is probably best left for another question where the pros/cons can be properly evaluated; but as always user preference will always play a part: none of the solutions presented are wrong, but some will look nicer to each individual coder.
addendum
This is an expanded solution of an earlier one I posted. Since that post kept getting attention, I decided to expand on it and refer to the other excellent solutions that were posted here. My original post had a remark that mentioned that if you were intending on modifying your vector inside a for
loop then there are two methods provided by std::vector
to access elements: std::vector::operator[]
which does not do bounds checking, and std::vector::at
which does perform bounds checking. In other words, at
will throw if you try to access an element outside the vector and operator[]
wouldn\'t. I only added this comment, originally, for the sake of mentioning something that it might be useful to know of if anyone already didn\'t. And I see no difference now. Hence this addendum.
回答2:
A much easier way to do this is with the standard copy algorithm:
#include <iostream>
#include <algorithm> // for copy
#include <iterator> // for ostream_iterator
#include <vector>
int main() {
/* Set up vector to hold chars a-z */
std::vector<char> path;
for (int ch = \'a\'; ch <= \'z\'; ++ch)
path.push_back(ch);
/* Print path vector to console */
std::copy(path.begin(), path.end(), std::ostream_iterator<char>(std::cout, \" \"));
return 0;
}
The ostream_iterator is what\'s called an iterator adaptor. It is templatized over the type to print out to the stream (in this case, char
). cout
(aka console output) is the stream we want to write to, and the space character (\" \"
) is what we want printed between each element stored in the vector.
This standard algorithm is powerful and so are many others. The power and flexibility the standard library gives you are what make it so great. Just imagine: you can print a vector to the console with just one line of code. You don\'t have to deal with special cases with the separator character. You don\'t need to worry about for-loops. The standard library does it all for you.
回答3:
In C++11 you can now use a range-based for loop:
for (auto const& c : path)
std::cout << c << \' \';
回答4:
I think the best way to do this is to just overload operator<<
by adding this function to your program:
#include <vector>
using std::vector;
#include <iostream>
using std::ostream;
template<typename T>
ostream& operator<< (ostream& out, const vector<T>& v) {
out << \"{\";
size_t last = v.size() - 1;
for(size_t i = 0; i < v.size(); ++i) {
out << v[i];
if (i != last)
out << \", \";
}
out << \"}\";
return out;
}
Then you can use the <<
operator on any possible vector, assuming its elements also have ostream& operator<<
defined:
vector<string> s = {\"first\", \"second\", \"third\"};
vector<bool> b = {true, false, true, false, false};
vector<int> i = {1, 2, 3, 4};
cout << s << endl;
cout << b << endl;
cout << i << endl;
Outputs:
{first, second, third}
{1, 0, 1, 0, 0}
{1, 2, 3, 4}
回答5:
How about for_each
+ lambda expression:
#include <vector>
#include <algorithm>
...
std::vector<char> vec;
...
std::for_each(
vec.cbegin(),
vec.cend(),
[] (const char c) {std::cout << c << \" \";}
);
...
Of course, a range-based for is the most elegant solution for this concrete task, but this one gives many other possibilities as well.
Explanation
The for_each
algorithm takes an input range and a callable object, calling this object on every element of the range. An input range is defined by two iterators. A callable object can be a function, a pointer to function, an object of a class which overloads () operator
or as in this case, a lambda expression. The parameter for this expression matches the type of the elements from vector.
The beauty of this implementation is the power you get from lambda expressions - you can use this approach for a lot more things than just printing the vector.
回答6:
The problem is probably in the previous loop: (x = 17; isalpha(firstsquare); x++)
. This loop will run not at all (if firstsquare
is non-alpha) or will run forever (if it is alpha). The reason is that firstsquare
doesn\'t change as x
is incremented.
回答7:
Just copy the container to the console.
std::vector<int> v{1,2,3,4};
std::copy(v.begin(),v.end(),std::ostream_iterator<int>(std::cout, \" \" ));
Should output :
1 2 3 4
回答8:
In C++11, a range-based for loop might be a good solution:
vector<char> items = {\'a\',\'b\',\'c\'};
for (char n : items)
cout << n << \' \';
Output:
a b c
回答9:
I see two problems. As pointed out in for (x = 17; isalpha(firstsquare); x++)
there\'s either an infinite loop or never executed at all, and also in if (entrance == \'S\')
if the entrance character is different than \'S\' then nothing in pushed to the path vector, making it empty and thus printing nothing on screen. You can test the latter checking for path.empty()
or printing path.size()
.
Either way, wouldn\'t it be better to use a string instead of a vector? You can access the string contents like an array as well, seek characters, extract substrings and print the string easily (without a loop).
Doing it all with strings might be the way to have it written in a less convoluted way and easier to spot the problem.
回答10:
overload operator<<:
template<typename OutStream, typename T>
OutStream& operator<< (OutStream& out, const vector<T>& v)
{
for (auto const& tmp : v)
out << tmp << \" \";
out << endl;
return out;
}
Usage:
vector <int> test {1,2,3};
wcout << test; // or any output stream
回答11:
Using std::copy
but without extra trailing separator
An alternative/modified approach using std::copy
(as originally used in @JoshuaKravtiz answer) but without including an additional trailing separator after the last element:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
template <typename T>
void print_contents(const std::vector<T>& v, const char * const separator = \" \")
{
if(!v.empty())
{
std::copy(v.begin(),
--v.end(),
std::ostream_iterator<T>(std::cout, separator));
std::cout << v.back() << \"\\n\";
}
}
// example usage
int main() {
std::vector<int> v{1, 2, 3, 4};
print_contents(v); // \'1 2 3 4\'
print_contents(v, \":\"); // \'1:2:3:4\'
v = {};
print_contents(v); // ... no std::cout
v = {1};
print_contents(v); // \'1\'
return 0;
}
Example usage applied to container of a custom POD type:
// includes and \'print_contents(...)\' as above ...
class Foo
{
int i;
friend std::ostream& operator<<(std::ostream& out, const Foo& obj);
public:
Foo(const int i) : i(i) {}
};
std::ostream& operator<<(std::ostream& out, const Foo& obj)
{
return out << \"foo_\" << obj.i;
}
int main() {
std::vector<Foo> v{1, 2, 3, 4};
print_contents(v); // \'foo_1 foo_2 foo_3 foo_4\'
print_contents(v, \":\"); // \'foo_1:foo_2:foo_3:foo_4\'
v = {};
print_contents(v); // ... no std::cout
v = {1};
print_contents(v); // \'foo_1\'
return 0;
}
回答12:
This answer is based on the answer from Zorawar, but i couldn\'t leave a comment there.
You can make the auto (C++11)/typedef version const by using cbegin and cend instead
for (auto i = path.cbegin(); i != path.cend(); ++i)
std::cout << *i << \' \';
回答13:
In C++11``
for (auto i = path.begin(); i != path.end(); ++i)
std::cout << *i << \' \';
for(int i=0; i<path.size(); ++i)
std::cout << path[i] << \' \';