小编典典

如何打印出向量的内容?

all

如何将 a 的内容打印std::vector到屏幕上?


实现以下内容的解决方案也operator<<很好:

template<container C, class T, String delim = ", ", String open = "[", String close = "]">
std::ostream & operator<<(std::ostream & o, const C<T> & x)
{
  // ... What can I write here?
}

这是我到目前为止所拥有的,没有单独的功能:

#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;
    }
}

阅读 133

收藏
2022-03-30

共1个答案

小编典典

如果您有 C++11 编译器,我建议使用基于范围的 for 循环(见下文);或者使用迭代器。但是你有几个选择,我将在下面解释所有这些。

基于范围的 for 循环 (C++11)

在 C++11(及更高版本)中,您可以使用新的基于范围的 for 循环,如下所示:

std::vector<char> path;
// ...
for (char i: path)
    std::cout << i << ' ';

for 循环语句中的类型char应该是向量元素的类型,path而不是整数索引类型。换句话说,因为pathis 是 type
std::vector<char>,所以应该出现在基于范围的 for 循环中的 type
char。但是,您可能经常会看到显式类型替换为auto占位符类型:

for (auto i: path)
    std::cout << i << ' ';

无论您使用显式类型还是auto关键字,对象i的值都是对象中实际项的副本pathi因此,循环中的所有更改path本身都不会保留:

std::vector<char> path{'a', 'b', 'c'};

for (auto i: path) {
    i = '_'; // 'i' is a copy of the element in 'path', so although
             // we can change 'i' here perfectly fine, the elements
             // of 'path' have not changed
    std::cout << i << ' '; // will print: "_ _ _"
}

for (auto i: path) {
    std::cout << i << ' '; // will print: "a b c"
}

如果你想禁止i在 for 循环中更改这个复制的值,你可以强制的类型iconst char这样的:

for (const auto i: path) {
    i = '_'; // this will now produce a compiler error
    std::cout << i << ' ';
}

如果您想修改其中的项目,path以便这些更改在pathfor 循环之外持续存在,那么您可以使用如下引用:

for (auto& i: path) {
    i = '_'; // changes to 'i' will now also change the
             // element in 'path' itself to that value
    std::cout << i << ' ';
}

即使您不想修改path,如果对象的复制成本很高,您应该使用 const 引用而不是按值复制:

for (const auto& i: path)
    std::cout << i << ' ';

迭代器

在 C++11 之前,规范的解决方案是使用迭代器,这仍然是完全可以接受的。它们的用法如下:

std::vector<char> path;
// ...
for (std::vector<char>::const_iterator i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

如果要在 for 循环中修改向量的内容,请使用iterator而不是const_iterator.

补充:typedef / 类型别名 (C11) / auto (C11)

这不是另一种解决方案,而是对上述iterator解决方案的补充。如果您使用的是 C++11
标准(或更高版本),那么您可以使用auto关键字来提高可读性:

for (auto i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

这里的类型i将是非常量的(即编译器将std::vector<char>::iterator用作的类型i)。这是因为我们调用了begin方法,所以编译器从中推断出类型i。如果我们cbegin改为调用该方法(“c”表示
const),那么i将是 a std::vector<char>::const_iterator

for (auto i = path.cbegin(); i != path.cend(); ++i) {
    *i = '_'; // will produce a compiler error
    std::cout << *i << ' ';
}

如果您对编译器推导类型不满意,那么在 C++11 中,您可以使用类型别名来避免必须一直输入向量(养成的好习惯):

using Path = std::vector<char>; // C++11 onwards only
Path path; // 'Path' is an alias for std::vector<char>
// ...
for (Path::const_iterator i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

如果您无权访问 C++11 编译器(或出于某种原因不喜欢类型别名语法),那么您可以使用更传统的typedef

typedef std::vector<char> Path; // 'Path' now a synonym for std::vector<char>
Path path;
// ...
for (Path::const_iterator i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

边注:

在这一点上,您以前可能遇到过迭代器,也可能没有,您可能听说过或可能没有听说过迭代器是您“应该”使用的,并且可能想知道为什么。答案并不容易理解,但简而言之,这个想法是迭代器是一种抽象,可以保护您免受操作细节的影响。

拥有一个执行您想要的操作(如顺序访问)的对象(迭代器)而不是您自己编写详细信息(“详细信息”是实际访问向量元素的代码)是很方便的。您应该注意到,在 for
循环中,您只是要求迭代器返回一个值(迭代器*i在哪里i)——您永远不会path直接与自身交互。逻辑是这样的:你创建一个迭代器并给它你想要循环的对象(iterator i = path.begin()),然后你所做的就是让迭代器为你获取下一个值(*i);您永远不必担心迭代器是如何做到这一点的——这是它的业务,而不是您的业务。

好的,但有什么意义呢?好吧,想象一下,如果获得价值并不简单。如果它涉及一些工作怎么办?你不必担心,因为迭代器已经为你处理了——它整理了细节,你需要做的就是向它询问一个值。此外,如果您将容器从std::vector其他东西更改怎么办?理论上,即使访问新容器中的元素的细节发生了变化,您的代码也不会改变:请记住,迭代器会在幕后为您整理所有细节,因此您根本不需要更改代码–
你只需向迭代器询问容器中的下一个值,和以前一样。

因此,虽然这看起来像是对遍历向量的混淆过度杀伤,但迭代器的概念背后有充分的理由,因此您不妨习惯使用它们。

索引

您还可以使用整数类型在 for 循环中显式地索引向量的元素:

for (int i=0; i<path.size(); ++i)
    std::cout << path[i] << ' ';

如果您打算这样做,最好使用容器的成员类型,如果它们可用且合适的话。std::vector有一个size_type为此作业调用的成员类型:它是size方法返回的类型。

typedef std::vector<char> Path; // 'Path' now a synonym for std::vector<char>
for (Path::size_type i=0; i<path.size(); ++i)
    std::cout << path[i] << ' ';

为什么不优先使用它而不是iterator解决方案?对于简单的情况,您可以这样做,但使用
aniterator会带来一些优势,我在上面已经简要概述了这些优势。因此,我的建议是避免这种方法,除非你有充分的理由。

标准::复制 (C++11)

约书亚的回答。您可以使用 STL
算法std::copy将矢量内容复制到输出流中。我没有什么要补充的,只是说我不使用这种方法;但除了习惯之外,没有什么好的理由。

std::ranges::copy (C++20)

为了完整起见,C++20 引入了范围,它可以作用于 a 的整个范围std::vector,因此不需要beginand end

#include <iterator> // for std::ostream_iterator
#include <algorithm> // for std::ranges::copy depending on lib support

std::vector<char> path;
// ...
std::ranges::copy(path, std::ostream_iterator<char>(std::cout, " "));

除非您有最新的编译器(显然在 GCC 上至少版本
10.1
),否则即使您可能有一些可用的
C++20 功能,您也可能不会获得范围支持。

重载 std::ostream::operator<<

另请参阅下面的回答]。这更像是对其他答案的补充,因为您仍然需要在重载中实现上述解决方案之一,但好处是代码更简洁。这就是您可以使用上述std::ranges::copy解决方案的方式:

#include <iostream>
#include <vector>
#include <iterator> // for std::ostream_iterator
#include <algorithm> // for std::ranges::copy depending on lib support

using Path = std::vector<char>; // type alias for std::vector<char>

std::ostream& operator<< (std::ostream& out, const Path& v) {
    if ( !v.empty() ) {
        out << '[';
        std::ranges::copy(v, std::ostream_iterator<char>(out, ", "));
        out << "\b\b]"; // use two ANSI backspace characters '\b' to overwrite final ", "
    }
    return out;
}

int main() {
    Path path{'/', 'f', 'o', 'o'};

    // will output: "path: [/, f, o, o]"
    std::cout << "path: " << path << std::endl;

    return 0;
}

现在您可以Path像基本类型一样将对象传递给输出流。使用上述任何其他解决方案也应该同样简单。

结论

此处介绍的任何解决方案都可以使用。哪个是“最好的”取决于您(以及上下文或您的编码标准)。任何比这更详细的东西可能最好留给另一个可以正确评估优点/缺点的问题,但一如既往,用户偏好总是会发挥作用:提出的解决方案都不是客观错误的,但有些解决方案对每个编码员来说看起来更好.

附录

这是我发布的早期解决方案的扩展解决方案。由于该帖子一直受到关注,因此我决定对其进行扩展并参考此处发布的其他出色解决方案,至少是我过去亲自使用过至少一次的那些。但是,我鼓励读者查看下面的答案,因为可能有一些我已经忘记或不知道的好建议。

2022-03-30