静态数组VS. C ++ 11中的动态数组

时间:2012-04-29 22:56:41

标签: c++ arrays templates c++11 move-semantics

我知道这是一场非常古老的辩论,已经在世界各地多次讨论过。但是我在解决在特定情况下我应该使用哪种方法而不是静态和动态数组之间的另一种方法时遇到麻烦。实际上,我没有使用过C ++ 11,我会使用静态数组。但我现在感到困惑,因为两者都有相同的好处。

第一个解决方案:

template<size_t N>
class Foo
{
    private:
        int array[N];

    public:
        // Some functions
}

第二个解决方案:

template<size_t N>
class Foo
{
    private:
        int* array;

    public:
        // Some functions
}

我不可能选择,因为这两者都有自己的优势:

  • 静态数组更快,我们根本不关心内存管理。
  • 只要未分配内存,动态数组就不会占用任何内容。之后,它们不像静态数组那样方便使用。但是从C ++ 11开始,我们可以从移动语义中获得很大的好处,我们不能将它用于静态数组。

我认为没有一个好的解决方案,但我想得到一些建议,或者只是想知道你对这一切的看法。

3 个答案:

答案 0 :(得分:5)

我实际上不同意“它取决于”。切勿使用选项2.如果要使用翻译时常量,请始终使用选项1或std :: array。你列出的一个优点是动态数组在分配之前没有任何重量,实际上是一个可怕的,巨大的劣势,需要特别强调指出。

不要有具有多个构造阶段的对象。永远不能。这应该是通过一些大纹身致力于记忆的规则。永远不要这样做。

当你拥有尚未生存的僵尸对象时,虽然还没有完全死亡,但管理其生命周期的复杂性呈指数级增长。您必须检查每个方法是否完全存活,或者只是假装活着。异常安全需要在析构函数中使用特殊情况。您现在已经添加了必须在N个不同位置(#methods + dtor)检查的要求,而不是一个简单的构造和自动销毁。如果你检查,编译器不在乎。其他工程师不会广播此要求,因此他们可能会以不安全的方式调整您的代码,使用变量而不进行检查。现在所有这些方法都有多种行为,具体取决于对象的状态,因此对象的每个用户都需要知道会发生什么。 僵尸会破坏你的(编码)生活。

相反,如果您的程序中有两个不同的自然生命周期,请使用两个不同的对象。但这意味着您的程序中有两种不同的状态,因此您应该拥有一个状态机,其中一个状态只有一个对象,另一个状态只有一个,由异步事件分隔。如果两点之间没有异步事件,如果它们都适合一个函数范围,则分离是人为的,你应该进行单相构造。

转换时间大小应转换为动态分配的唯一情况是当堆栈的大小太大时。然后进行内存优化,应始终使用内存和分析工具对其进行评估,以查看最佳内容。选项2永远不会是最好的(它使用裸指针 - 所以我们再次丢失RAII和任何自动清理和管理,添加不变量并使代码更复杂,并且容易被其他人破坏)。尽管您可能不喜欢堆分配成本,但Vector(由位掩码建议)将是首先考虑的。其他选项可能是应用程序图像中的静态空间。但同样,只有在您确定存在内存约束后才能考虑这些问题,并且应该根据实际可测量的需求来确定应该做些什么。

答案 1 :(得分:4)

两者都不使用。几乎在任何情况下,最好使用std::vector。在其他情况下,这在很大程度上取决于std::vector不足的原因,因此无法回答!

答案 2 :(得分:4)

  

我目前在确定在特定情况下应该使用哪一个比另一个更多的问题。

您需要根据具体情况考虑您的选项,以确定给定上下文的最佳解决方案 - 也就是说,无法进行泛化。如果一个容器对于每种情况都是理想的,那么另一种容器就会过时。

如前所述,在编写自己的实现之前,请考虑使用std实现。

更多详情:

固定长度

  • 小心你消耗了多少筹码。
  • 如果将其视为动态大小的容器,可能会占用更多内存。
  • 快速复制。

可变长度

  • 重新分配和调整大小可能会很昂贵。
  • 可能会消耗更多的内存。
  • 快速移动。

更好的选择还要求您了解元素类型的创建,复制,分配等的复杂性。

如果您使用std实现,请记住实现可能会有所不同。

最后,您可以为这些类型创建一个容器,它抽象实现细节,并根据大小和上下文动态选择适当的数据成员 - 抽象一般接口背后的细节。这有时也可用于禁用功能,或使某些操作(例如昂贵的副本)更加明显。

简而言之,您需要了解很多类型和用法,并测量程序的几个方面,以确定特定方案的最佳容器类型。