【问题标题】:destructor implementation in templated class模板类中的析构函数实现
【发布时间】:2017-04-14 12:48:03
【问题描述】:

如果我有这样的课程:

template <class T>                            
class Array {                                              
 private:                                        
  T *m_pData;                                   
  unsigned int m_nSize;                         
 public:                                         
 Array(unsigned int nSize) : m_nSize(nSize),m_pData(nullptr) {                                           
 if(m_nSize > 0)
  m_pData = new T[m_nSize];                
 }                                           

 ~Array() {                    
  if(m_pData != NULL) {
   delete [] m_pData;
  }
};

如果现在我像这样创建我的类的对象:

Array<int> miArray(5);

析构函数的实现应该没问题,但是如果我创建这样的对象:

Array<int*> miArray(5);

在析构函数中,我应该删除存储在数组中的每个对象以避免内存泄漏。

我怎样才能做到这一点?

谢谢

【问题讨论】:

标签: c++ class templates destructor delete-operator


【解决方案1】:

您可以使用a specialization of your destructor,或标记调度到删除功能(这会更灵活)。然而,这导致了一个非常尴尬和脆弱的设计。假设你已经实现了一个等价于push_back,考虑这些用户代码sn-ps:

{
    Array<int*> arr;
    arr.push_back(new int(42)); // new is on the side of the user
} // ... but delete is not. Weird.

这也会导致一整类错误:您只能将newed Ts 给Array&lt;T*&gt;,但是对于传递其他内容没有任何安全措施:

{
    Array<int*> arr;
    int i = 42;
    arr.push_back(&i); // No diagnostic
    arr.push_back(new int[17]); // No diagnostic either
} // Undefined behaviour from calling `delete` on stuff that hasn't been `new`ed

所有这些都是没有原始拥有指针规则存在的原因:原始指针根本不应该管理资源生命周期。如果我使用Array&lt;int*&gt;,它应该存储我的指针并让我使用它们,但永远不要永远 delete 它们,因为这是试图管理生命周期。

相反,如果我想要一个管理生命周期的Array,我将使用相应的智能指针,即Array&lt;std::unique_ptr&lt;int&gt;&gt;。这与Array 很好地联系在一起,要求它只调用所包含对象的析构函数(它已经这样做了)。这些析构函数(即~unique_ptr)将按照它们的工作传递地释放资源,一切都很好。

补充说明:

  • 如果需要,请注意初始化Array 的缓冲区——m_pData = new T[m_nSize]; 将分配默认初始化的对象。如果这些对象没有默认构造函数,它们的值将是不确定的。一个简单的解决方法是使用new T[m_nSize]{},它将执行值初始化——也就是说,将算术类型初始化为零,将指针初始化为nullptr,并递归地复合类型。

  • 为您的类实现复制和/或移动语义,请参阅Rule of three/five/zero。就像现在一样,复制Array 的实例将导致两个实例认为它们拥有相同的缓冲区,并且当两者都尝试delete 它时会出现未定义的行为。

  • deletedelete 检查 null,因此析构函数中的 if(m_pData != NULL) 是多余的。

祝你好运:)

【讨论】:

    【解决方案2】:

    我认为一个好的设计是在 smart 指针类中包装原始 owning 指针(例如您的示例中的 int*),例如 std::unique_ptr 或 @ 987654323@,或其他一些 RAII 包装器。 这就是 std::vector 的基本情况,如果你想在向量中存储指向 T 的拥有指针,你可以使用 vector&lt;unique_ptr&lt;T&gt;&gt;vector&lt;shared_ptr&lt;T&gt;&gt;

    请注意,在容器中存储原始 observing 指针是可以的(只要正确处理指向对象的生命周期)。


    只是补充几点说明:

    1. 您的类应该禁止通过=delete 复制构造函数和复制赋值进行复制,或者正确实现这些复制操作。由于它处于当前状态,如果人们尝试复制类的构造或分配实例,则很容易出错。

    2. Array(unsigned int nSize) 构造函数应标记为explicit,以避免从无符号整数进行隐式转换。

    【讨论】:

      猜你喜欢
      • 2020-08-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-21
      • 1970-01-01
      • 2018-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多