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

Static arrays VS. dynamic arrays in C++11

本文关键字:数组 动态 C++11 VS 静态      更新时间:2023-10-16

我知道这是一个非常古老的辩论,已经在世界各地讨论了很多次。但是我目前在决定在特定情况下应该使用哪种方法而不是在静态和动态数组之间使用另一种方法时遇到麻烦。实际上,我没有使用 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 开始,我们可以从移动语义中获得巨大的好处,我们不能将其用于静态数组。

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

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

永远不要有具有多个构造阶段的对象。 从来没有,从来没有。 这应该是通过一些大纹身来记忆的规则。 只是永远不要这样做。

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

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

转换时间大小应转换为动态分配的唯一情况是当大小对于堆栈来说太大时。 然后进行内存优化,应始终使用内存和分析工具对其进行评估,以查看最佳方法。 选项 2 永远不会是最好的(它使用裸指针 - 因此我们再次丢失 RAII 以及任何自动清理和管理,添加不变量并使代码更复杂且容易被其他人破解(。 矢量(如位掩码所建议的(将是适当的第一个想法,尽管您可能不喜欢及时的堆分配成本。 其他选项可能是应用程序映像中的静态空间。 但同样,只有在您确定存在内存限制后,才应考虑这些限制,并且从那里做什么应该由实际可衡量的需求决定。

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

我目前在决定在特定情况下应该比另一个更使用哪一个时遇到问题。

您需要逐案考虑您的选项,以确定给定上下文的最佳解决方案——也就是说,无法进行概括。如果一个容器适合每种方案,那么另一个容器就会过时。

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

更多详情:

固定长度

  • 请注意消耗多少堆栈。
  • 如果将其视为动态大小的容器,则可能会消耗更多内存。
  • 快速复制。

可变长度

  • 重新分配和调整大小可能代价高昂。
  • 可能会消耗比所需更多的内存。
  • 快速移动。

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

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

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

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