避免对非虚拟析构函数进行对象切片

时间:2013-04-08 18:46:12

标签: c++ templates pointers g++ smart-pointers

我正在为智能指针编写代码作为练习。在线使用教程(12)我开发了一个带引用计数的普通智能指针类。问题是我无法弄清楚以下内容:

  

当智能指针检测到a不存在更多引用时   特殊对象,它必须通过指针删除对象   原始类型,即使最终智能的模板参数   指针是基本类型。这是为了避免对象切片   非虚拟析构函数。

我怎样才能做到这一点。基本上我的代码如下所示(来自教程)。

template < typename T > class SP
{
private:
    T*    pData;       // pointer
    RC* reference; // Reference count

public:
    SP() : pData(0), reference(0) 
    {
        // Create a new reference 
        reference = new RC();
        // Increment the reference count
        reference->AddRef();
    }

    SP(T* pValue) : pData(pValue), reference(0)
    {
        // Create a new reference 
        reference = new RC();
        // Increment the reference count
        reference->AddRef();
    }

    SP(const SP<T>& sp) : pData(sp.pData), reference(sp.reference)
    {
        // Copy constructor
        // Copy the data and reference pointer
        // and increment the reference count
        reference->AddRef();
    }

    ~SP()
    {
        // Destructor
        // Decrement the reference count
        // if reference become zero delete the data
        if(reference->Release() == 0)
        {
            delete pData;
            delete reference;
        }
    }

    T& operator* ()
    {
        return *pData;
    }

    T* operator-> ()
    {
        return pData;
    }

    SP<T>& operator = (const SP<T>& sp)
    {
        // Assignment operator
        if (this != &sp) // Avoid self assignment
        {
            // Decrement the old reference count
            // if reference become zero delete the old data
            if(reference->Release() == 0)
            {
                delete pData;
                delete reference;
            }

            // Copy the data and reference pointer
            // and increment the reference count
            pData = sp.pData;
            reference = sp.reference;
            reference->AddRef();
        }
        return *this;
    }
};

修改

要实现这一点,我必须有一个指向原始类型的指针。

我在此处发布了一个问题:delete via a pointer to Derived, not Base

但是现在查看评论和答案后,我认为两者都是相关的。我有构造函数:

template <typename T>
template <typename U>
Sptr<T>::Sptr(U* u) : obj(u),ref(NULL) {
    //do something
    ref = new RC();
    ref->AddRef();
}

现在考虑Sptr<Base1> sp(new Derived);其中Derived来自Base1。 Base1具有受保护的构造函数/析构函数。 这是为T类型的对象存储但我需要通过U类型的对象存储它。我需要保留它。我怎么能这样做?

2 个答案:

答案 0 :(得分:6)

您的智能指针需要3个信息块。

首先,指向数据的指针(T*或其他东西)。

其次,您的引用次数为:std::atomic<int>或其他。

第三,你的销毁功能(std::function<void(T*)>或其他)。

首次创建智能指针时,会创建该销毁功能。将智能指针复制到另一个智能指针时,将复制此销毁功能。如果新智能指针的类型与旧指针不匹配,那么该销毁函数将以类型兼容的方式进行封装(std::function<void(Base*)> = std::function<void(Derived*)>是否可以开箱即用?无论如何,您基本上都是这样做的。)< / p>

默认情况下,此销毁函数仅为delete t,但作为附带好处,这允许智能指针的用户传递销毁函数,该函数并非总是delete t。 / p>

有趣的是,相当于reset,你可以替换你的销毁功能。因此,您实际上可以使销毁函数的签名为std::function<void()>,这使得在TU类型智能指针之间移动它更容易。

template < typename T > class SP
{
private:
  T*    pData;       // pointer
  RC* reference; // Reference count
  std::function<void()> destroyData;
public:
  template<typename U>
  SP(U* pValue):
    pData(pValue),
    reference(nullptr),
    // store how to destroy pValue now, for later execution:
    destroyData([pValue]()->void{
      delete pValue;
    })
  {
    // Create a new reference 
    reference = new RC();
    // Increment the reference count
    reference->AddRef();
  }
  // similar for operator=, and you may have to do something for SP<T> as well:
  template<typename U>
  SP(const SP<U>& sp):
    pData(sp.pData),
    reference(sp.reference),
    destroyData(sp.destroyData)
  {
    // Copy constructor
    // Copy the data and reference pointer
    // and increment the reference count
    reference->AddRef();
  }
  template<typename U>
  SP<T>& operator = (const SP<U>& sp)
  {
    // blah blah blah, then
    destroyData = sp.destroyData;
  }

  ~SP()
  {
    // Destructor
    // Decrement the reference count
    // if reference become zero delete the data
    if(reference->Release() == 0)
    {
        delete reference;
        destroyData(); // here I destroyed it!
    }
  }
};

或类似的东西

答案 1 :(得分:0)

另一种方法是将删除委托给另一个类

// non templated base
class DeleterBase {
    public:
        virtual ~DeleterBase() { };
};

template <typename T>
class Deleter : public DeleterBase {
    private:
        T *ptr;
    public:
        Deleter(T *p) // remember the pointer with the correct type here
            : ptr{p}
        { }

        ~Deleter() { 
            delete this->ptr; // invokes correct destructor
        }
};

现在在智能指针中:

template <typename T>
class SP {
    private:
        T *p;
        DeleterBase *deleter;
    public:
        template <typename U> // U is deduced to actual type
        explicit SP(U *p)
            : p{p},
            deleter{new Deleter<U>(p)} // correct type
        { }

        // transfer control in the move constructor/assignment
        // increase the ref count in copy constructor/assignment

        // now just delete the deleter in the dtor
        ~SP() {
            if (reference_count_is_zero()) { // however you implement this
                delete this->deleter;
            }
        }
};

由于您已经拥有RC类,因此您可以将我在此处显示的功能移动到该类中。使用非模板化基础对该类进行模板化,并在删除引用计数时销毁该指针。少了一件事。