【问题标题】:Controlling memory with std::vector<T*>用 std::vector<T*> 控制内存
【发布时间】:2014-02-14 05:05:53
【问题描述】:

假设T 包含一个数组,其大小可能因初始化而异。我正在传递一个指向向量的指针以避免复制所有数据,并初始化如下:

for(int i=10; i < 100; i++)
    std::vector.push_back(new T(i));

退出时,一个deletes 是向量的元素。如果T 中包含的数据也是一个指针,即使有好的析构函数,是否存在内存丢失的风险?例如

template<class M> class T{
    M * Array;
public:
    T(int i) : Array(new M[i]){ }
    ~T(){ delete Array;}
};

【问题讨论】:

  • 为什么不在T 中使用矢量,省去自己处理Rule of Three 的麻烦?为什么不使用vector&lt;T&gt; 来省去删除元素时删除元素的麻烦?
  • 所以,T 有一个M 类型的底层数组,并且您将指向T 的指针存储在std::vector 中?为什么不直接使用std::vector&lt;std::vector&lt;M&gt;&gt; 并避免所有不必要的手动内存管理?
  • 另外,如果您可以emplace_back 项目,您也将避免复制内容。
  • 您到底想做什么?并且不要说存储指向包含指向数组的指针的类的指针向量。对于任何问题,这都是一个糟糕的解决方案,而不是你现在想解决的问题。
  • @rubenvb:从代码来看,OP 的目标是简单地拥有一个动态数组的动态数组,存储M 的实例,即std::vector&lt;std::vector&lt;M&gt;&gt;,但可能由于现有代码,卡住了使用T 分配Ms 的C 数组。

标签: c++ pointers memory-management vector


【解决方案1】:

你的班级T有两个主要问题:

  • 您使用delete 而不是delete [] 来删除数组,导致未定义的行为
  • 您没有实现(或删除)复制构造函数和复制赋值运算符(根据Rule of Three),因此存在两个对象都试图删除同一个数组的危险。

这两个问题都可以通过使用std::vector 轻松解决,而不是编写自己的版本。

最后,除非您有充分的理由(例如多态性)来存储指针,否则请使用std::vector&lt;T&gt;,这样您就不需要手动删除元素。在删除元素或离开向量范围时很容易忘记这样做,尤其是在抛出异常时。 (如果您确实需要指针,请考虑使用unique_ptr 自动删除对象)。

【讨论】:

    【解决方案2】:

    答案是:不要。

    任意使用

    std::vector<std::vector<M>> v;
    v.emplace_back(std::vector<M>(42)); // vector of 42 elements
    

    或(讨厌)

    std::vector<std::unique_ptr<M[]>> v;
    // C++11
    std::unique_ptr<M[]> temp = new M[42]; // array of 42 elements
    v.emplace_back(temp);
    // C++14 or with handrolled make_unique
    v.emplace_back(std::make_unique<M[]>(42);
    

    它们都以最小的开销为您做所有事情(尤其是最后一个)。

    请注意,使用new 参数调用emplace_back 并不像您想要的那样安全,即使结果元素是智能指针也是如此。为此,您需要使用 C++14 中的std::make_unique。存在各种实现,它不需要什么特别的。它只是从 C++11 中省略了,将添加到 C++14 中。

    【讨论】:

    • 由于在 M 数组的构造和其 unique_ptr 包装器的构造之间可能发生异常,是否存在异常安全问题?
    • @JAB 是的,当向量在调整大小以容纳新元素时抛出,并让您分配的内存遭受痛苦并孤独地死去,就会发生这种情况。请参阅 cmets here 进行讨论,并参阅 here 了解更多关于它何时会咬你的示例。
    • @rubenvb 那么std::make_unique(new M[42])std::unique_ptr&lt;M[]&gt;(new M[42]) 有什么区别呢? make_unique 只有当它没有获得分配的指针,而是调用 new 本身时才会更安全。另外,当它只是一个参数时,构造临时unique_ptr和使用make_unique没有区别,使用后者只会使代码更加一致。
    • @KonstantinOznobihin 您对make_unique 的调用是正确的(我已修复了调用),但在其有用性方面却没有。请参阅here,了解与这种情况有关的问题。
    • @rubenvb 根本没有,在上述情况下,emplace_back(new T)emplace_back(unique_ptr&lt;T&gt;(new T)) 完全不同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-21
    • 2017-09-01
    • 2018-02-02
    • 2019-10-20
    • 2016-03-31
    • 2020-10-11
    • 2019-11-07
    相关资源
    最近更新 更多