简体   繁体   English

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

[英]Avoid object slicing for non-virtual destructors

I am writing code for smart pointers as an exercise. 我正在为智能指针编写代码作为练习。 Using tutorials online ( 1 , 2 ) I have developed a normal smart-pointer class with reference counting. 使用在线教程( 12 )我已经与引用计数正常的智能指针类。 The problem is I am unable to figure out the following: 问题是我无法弄清楚以下内容:

when the smart pointer detects that no more references exist to a particular object, it must delete the object via a pointer to the original type, even if the template argument of the final smart pointer is of a base type. 当智能指针检测到特定对象不再存在引用时,它必须通过指向原始类型的指针删除该对象,即使最终智能指针的模板参数是基类型也是如此。 This is to avoid object slicing for non-virtual destructors. 这是为了避免非虚拟析构函数的对象切片。

How can I achieve that. 我怎样才能做到这一点。 Basically my code looks like below (from the tutorial). 基本上我的代码如下所示(来自教程)。

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;
    }
};

EDIT: 编辑:

To achieve that I have to have a pointer to the original type. 要实现这一点,我必须有一个指向原始类型的指针。

I have posted a question here: delete via a pointer to Derived, not Base 我在这里发了一个问题: 通过指向Derived的指针删除,而不是Base

But Now since viewing the comments and answers I think both are related. 但是现在查看评论和答案后我认为两者都是相关的。 I have the constructor: 我有构造函数:

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

Now consider Sptr<Base1> sp(new Derived); 现在考虑Sptr<Base1> sp(new Derived); where Derived is derived from Base1 . Derived派生自Base1 Base1 has protected constructor/destructor. Base1具有受保护的构造函数/析构函数。 Which is storing for an object of type T But I need to store it through an object of type U. I need to preserve that. 这是为T类型的对象存储但我需要通过U类型的对象存储它。我需要保留它。 How can I do that? 我怎样才能做到这一点?

Your smart pointer needs 3 chunks of information. 您的智能指针需要3块信息。

First, the pointer to the data ( T* or something). 首先,指向数据的指针( T*或其他)。

Second, your reference count: std::atomic<int> or something. 第二,你的引用计数: std::atomic<int>或者什么。

Third, your destruction function ( std::function<void(T*)> or something). 第三,你的销毁功能( std::function<void(T*)>或者什么)。

When the smart pointer is first created, that destruction function is created. 首次创建智能指针时,会创建该销毁功能。 When your smart pointer is copied to another smart pointer, this destruction function is copied. 将智能指针复制到另一个智能指针时,将复制此销毁功能。 If the type of the new smart pointer doesn't match the old, that destruction function gets wrapped up in a type-compatible way (does std::function<void(Base*)> = std::function<void(Derived*)> work out of the box? Regardless, you are basically doing that). 如果新智能指针的类型与旧指针不匹配,则该销毁函数以类型兼容的方式被包装( std::function<void(Base*)> = std::function<void(Derived*)>开箱即用?无论如何,你基本上都是这样做的)。

By default, this destruction function is just delete t , but as a side benefit, this allows the users of your smart pointer to pass in a destruction function, which isn't always delete t . 默认情况下,此销毁功能只是delete t ,但作为附带好处,这允许智能指针的用户传递销毁功能,该功能并不总是delete t

Amusingly, on the equivalent of reset , you replace your destruction function. 有趣的是,相当于reset ,你替换你的销毁功能。 So you could actually make the signature of the destruction function be std::function<void()> , which makes moving it between T and U type smart pointers a tad easier. 因此,您实际上可以使销毁函数的签名为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!
    }
  }
};

or something like that 或类似的东西

An alternative approach involves delegating the deletion another class 另一种方法是将删除委托给另一个类

// 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
        }
};

now in the smart pointer: 现在在智能指针中:

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;
            }
        }
};

Since you already have the RC class you could move the functionality I've shown here into that class. 由于您已经拥有RC类 ,因此可以将我在此处显示的功能移动到该类中。 Make that class templated with a non-templated base, and destroy the pointer when it's reference count is deleted. 使用非模板化基础对该类进行模板化,并在删除引用计数时销毁该指针。 One less thing to pass around. 少走过一点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM