【问题标题】:Can std::array (or boost::array) be used in this case, or am I stuck with std::vector and native arrays?在这种情况下可以使用 std::array (或 boost::array ),还是我坚持使用 std::vector 和本机数组?
【发布时间】:2013-07-09 23:30:54
【问题描述】:

所以 std::array 和 boost::array (它们几乎相同,在下文中我将模糊地称为“数组”)旨在为数组提供一个容器对象,该对象不会产生向量的开销如果数组不动态改变大小,则不需要。但是,它们都是通过将数组大小不作为构造函数参数而是模板参数来设计的。结果:vector 允许在对象创建后动态调整大小;数组需要在编译时知道大小。

据我所知,如果您有一个数组,您将在创建对象时知道其大小,但在编译时不知道,那么您唯一的选择是 1) 使用向量会产生不必要的额外开销,2) 使用(非容器类型)原生数组(例如,int foo[42];),或者 3)从头开始编写自己的数组包装类。那么这是正确的吗,这是一种介于两者之间的情况,您可能想使用数组而不是向量,但不能?或者有什么我可以做的魔法对我有用吗?

这里有一些关于启发这个问题的细节(好的,很多),以防它帮助你理解:

我有一个模块 - 比如说调用者 - 它将在运行时重复生成二进制数据(无符号字符 [] 或数组),然后将它传递给另一个模块 - 比如说被调用者。被调用者模块不会修改数组(它会复制并在必要时进行修改),所以一旦调用者模块最初创建了数组,它就不会改变大小(也不会改变内容)。但是,出现了两个问题:1)调用者可能不会在每次生成数组时都生成相同大小的数组——它会在创建数组时在 rutime 时知道数组大小,但在编译时不知道。 2)调用者将数组传递给被调用者的方法需要能够获取调用者传递给它的任何大小的数组。

我想把它做成一个模板函数,例如,

template<size_t N> void foo(const array<unsigned char, N>& my_array);

但是,我使用接口类将接口与被调用模块中的实现分开。因此,该函数必须是一个虚方法,它与被模板化是互斥的。此外,即使这不是问题,它仍然会遇到与上述 #1 相同的问题 - 如果在编译时不知道数组大小,那么它也无法在编译时解析模板化函数。

我的实际功能:

virtual void foo(const array<unsigned char, N>& my_array); // but what is N???

总之,我是否正确,我唯一真正的选择是使用向量或原生数组,例如,

virtual void foo(const vector<unsigned char> my_array); // unnecessary overhead
virtual void foo(const unsigned char[] my_array, size_t my_array_len); // yuk

或者是否有一些我忽略的技巧可以让我使用 std::array 或 boost::array?

【问题讨论】:

  • C++14 有 std::dynarray 和运行时大小的数组。
  • 您是否确定了使用std::vector 需要多少开销,并将其确定为问题?我问是因为它的开销真的很小(如果你使用得当,每个容器有 1 个额外的指针):std::dynarrays 最大的优势实际上是基于堆栈的存储的可能性。
  • 是的,事实是 st​​d::vector 的开销可能不会杀死我......这就是我现在要依靠的。

标签: c++ arrays boost vector stl


【解决方案1】:

直到我们在 C++11 中有 std::dynarray,你可以使用 std::unique_ptr

std::unique_ptr<Foo[]> arr(new Foo[100]);

您可以将其用作arr[0]arr[1] 等,它会在销毁时调用正确的delete[]。开销很小(只是指针)。

我认为数组类型的唯一指针和std::dynarray 之间的唯一区别是后者具有迭代器和size 其他“容器”属性,并且它将位于“容器”部分而不是“通用公用事业”。 [更新:编译器可能会选择原生支持dynarray 并对其进行优化以使用堆栈存储。]

【讨论】:

  • std::dynarray 背后的动机是它可以选择性地从堆栈分配内存。这是 C99s 可变长度数组的替代品。 dynarray 可能是 C++14
  • @RareBox:另外,C++14有可变长度数组。 (但你说得对,N2648 建议为 std::dynarray 分配堆栈作为 QoI。)
【解决方案2】:

如果您在编译时不知道长度,就不能使用任何形式的 std::array。

如果您在编译时不知道数组的大小,请认真考虑使用 std::vector。使用可变长度数组(如int foo[n])不是标准的 C++,如果给定的长度足够大,会导致堆栈溢出。此外,您不能编写任何(可测量的)开销小于 std::vector 的类数组包装器。

我会用

virtual void foo(const unsigned char* my_array, size_t my_array_len);

然后这样称呼它

obj.foo(&vec[0], vec.size());

没有附加开销,它可以满足您的需求。除了普通数组 (int foo[42]) 之外,它还可以使用向量和 std::arrays 调用,开销为零。

【讨论】:

  • /好吧,您可以编写一个比std::vector 少一点但可测量的开销。要使它像std::vector 一样坚固,需要付出很多努力,并且您可以为每个容器保存一个指针。除非您有 1000000 个 vectors 每个都包含少量数据(0 到 3 个指针大小或更小的元素),或者正在嵌入式系统上进行开发,否则黑盒注意到差异将是一个挑战。
  • 感谢@RareBox 的回答。我试图避免这种情况,因为 my_array 和 my_array_len 不是同一个对象的一部分,这使得调用者需要跟踪两件事。 std::vector 和 std::array 为您跟踪大小,因此作为被调用者,我可以使用 size() 方法并确定我得到了正确的答案。所以我现在决定只使用向量并产生开销,并且只有当我看到性能问题(在这种情况下是可疑的,老实说)时,我才会使用 char* 和 size 参数方法。
【解决方案3】:

其他注意事项:

  • 数组在堆栈上分配。这比在堆上分配快很多
  • 数组总是在创建时初始化其所有元素。

所以:

    class Foo;
    std::array<Foo, 100> aFoo;

构造 100 个 Foo 对象,(调用 Foo::Foo() 100 次)同时

    std::vector<Foo> vFoo;
    vFoo.reserve(100);

为 100 个Foo 对象(在堆上)保留空间,但不构造任何一个。

【讨论】:

    猜你喜欢
    • 2014-08-20
    • 2012-12-08
    • 2020-05-21
    • 2021-05-26
    • 2017-04-17
    • 1970-01-01
    • 2021-03-21
    • 1970-01-01
    • 2016-02-12
    相关资源
    最近更新 更多