【问题标题】:Deleting a Template Type删除模板类型
【发布时间】:2016-05-19 17:23:54
【问题描述】:

我有一个看起来像这样的通用类:

template <class T>
class Example
{
  private:
    T data;
  public:
    Example(): data(T())
    Example(T typeData): data(typeData) 
    ~Example()

    // ...
};

我有点困惑如何为这样的事情实现解构器。具体来说,由于T 是任何类型的,它可能是分配在堆栈上的内存(对于Example 通过无参数构造函数创建的情况总是如此)或堆上。

例如,如果客户端将T 的类型设为int* 并提供指向动态内存的指针,我怎么知道在data 上调用delete 而不是客户端设置类型给int

【问题讨论】:

  • 好吧,你可以像标准容器一样而不是那样做。如果类型是指针类型,标准容器析构函数不会对元素做任何事情。
  • 提示:使用基于 std::is_pointer&lt;T&gt; 的 CRTP 策略类型惯用语。享受构建它的乐趣,然后将整个东西装箱,因为在使用时很难区分指向自动变量的指针和动态变量的指针。
  • 两个有效点。我正在做我认为更像是一种练习的东西,所以我最终只是想知道我是否遗漏了什么,或者这是否只是一件很难解决的事情(似乎是这样)

标签: c++ class templates


【解决方案1】:

最简单的答案是:不要。不要试图猜测用户并做他们可能没想到的事情。采用与标准容器相同的策略:假设 T 正确清理自身。

如果客户端代码编写正确,它将使用 RAII 类(例如智能指针)来自动正确地管理内存和其他资源。如果不是,您就不能希望在您的提供程序代码中修复它。

让您的课程与std::unique_ptrstd::shared_ptr 以及任何其他自定义RAII 课程一起工作,并让您的客户自己进行管理。毕竟,如果他们想存储非拥有指针怎么办?

【讨论】:

  • 很公平——我管理清理与客户之间的模棱两可是我真正关心的问题。
【解决方案2】:

您可以使用模板专业化。

template <class T>
class Example
{
  private:
    T data;
  public:
    Example()
        : data(T())
    {}

    Example(T typeData): data(typeData)
    {}
};

template <class T>
class Example<T*>
{
  private:
    T* data;
  public:
    Example() : data(nullptr){}
    Example(T* typeData): data(typeData) {}
    ~Example()
    {
        delete data;
    }
};

int main() 
{
    Example<int> e;
    Example<int*> e2;

    return 0;
}

【讨论】:

  • 很酷的模式。以这种方式记录行为要容易得多。
  • 我会小心在生产代码中使用它。正如其他人所说:很难判断一个指针是否指向newed存储。
【解决方案3】:

您不能像标准库那样担心它。例如,如果您创建一个指针向量,您有责任在让向量超出范围之前删除它们。然后人们可以决定他们是否甚至希望它被删除(也许它是临时的排序并且其他东西拥有该对象)。他们还可以使用智能指针,以便vector通过智能指针的析构函数销毁对象。

在这种情况下,少即是多。你不必做任何复杂的事情。您不必维护模板的多个版本。最后,您的模板的用户拥有更多的控制权......当然也有责任。

【讨论】:

    【解决方案4】:

    使用可以按类型选择的内存释放助手模板类。 您不需要使用模板专业化来复制您的课程。 你只能写一个类。

    #include <type_traits>
    
    template<typename T>   // primary template
    struct Releaser
    {
      template<typename V>
        void release( V v ) { }
    };
    template<>  // explicit specialization for T = std::true_type
    struct Releaser<std::true_type>
    {
      template<typename V>
        void release( V v ) { delete[] v; }
    };
    
    template <class T>
    class Example
    {
      private:
        T data;
      public:
        Example(): data(T()) {}
        Example(T typeData): data(typeData) {}
        typedef typename std::is_pointer<T>::value_type isptr_type;
        ~Example() {
          Releaser< isptr_type >::release( data );
        }
    };
    

    但是需要知道new调用的形式,所以使用delete或者delete[]。

    【讨论】:

      【解决方案5】:

      当你需要Example 持有一个拥有指针时,我建议你使用std::unique_ptr 代替T。如果T 是一个原始指针,那么它根本不属于它,不应该删除它。

      如果您需要Example 来初始化指针,请将其特化为std::unique_ptr 并在默认构造函数中调用std::make_unique

      template<typename T>
      class Example<std::unique_ptr<T>> {
          Example() : data{std::make_unique<T>()} {}
      
          /* rest of the class */
      };
      

      如果你这样做,你不应该专门为T* 做一个new 的类,因为你不能初始化非拥有指针。你应该在你的构造函数中接收它,如果你不希望它为空,可以禁用原始指针的默认构造函数。

      template<typename T>
      class Example<T*> {
          Example() = delete;
          Example(T* data_) : data{data_}
      
          /* data is not an owning pointer. No need for a destructor */
      
          /* rest of the class */
      };
      

      如果你遵循这些规则,你应该不会有内存管理的问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-14
        • 1970-01-01
        • 1970-01-01
        • 2014-11-19
        • 2018-02-19
        • 2012-10-30
        相关资源
        最近更新 更多