避免对非虚拟析构函数进行对象切片 [英] Avoid object slicing for non-virtual destructors

查看:220
本文介绍了避免对非虚拟析构函数进行对象切片的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为智能指针编写代码作为练习。在线使用教程( 1 2 )我开发了一个具有引用计数的普通智能指针类。问题是我找不到以下内容:


当智能指针检测到没有更多引用存在
时特定对象时,即使最终智能
指针的模板参数是基本类型,它也必须通过指向
原始类型的指针来删除该对象。这是为了避免对
非虚拟析构函数进行对象分片。


我该如何实现这一点。基本上我的代码如下所示(来自教程)。

  template< typename T> SP 
{
private:
T * pData; //指针
RC *引用; //引用计数

public:
SP():pData(0),reference(0)
{
//创建一个新引用
reference = new RC();
//增加引用计数
reference-> AddRef();


SP(T * pValue):pData(pValue),reference(0)
{
//创建一个新的引用
reference =新的RC();
//增加引用计数
reference-> AddRef();


SP(const SP< T& sp):pData(sp.pData),reference(sp.reference)
{
// Copy构造函数
//复制数据和引用指针
//并增加引用计数
reference-> AddRef();

$ b〜SP()
{
//析构函数
//减少引用计数
//如果引用变为零删除数据
if(reference-> Release()== 0)
{
delete pData;
删除参考;
}
}

T&运算符*()
{
return * pData;
}

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

SP< T>& operator =(const SP< T& sp)
{
//赋值运算符
if(this!=& sp)//避免自赋值
{
//减少旧的引用计数
//如果引用变为零删除旧的数据
if(reference-> Release()== 0)
{
delete的pData;
删除参考;
}

//复制数据和引用指针
//并增加引用计数
pData = sp.pData;
reference = sp.reference;
reference-> AddRef();
}
return * this;
}
};

编辑:

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


我在这里发布了一个问题:

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

  template< typename T> 
模板< typename U>
Sptr< T> :: Sptr(U * u):obj(u),ref(NULL){
//做某事
ref = new RC();
ref-> AddRef();
}

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

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

首先,指向数据的指针( T * 或其他)。引用计数: std :: atomic< int> 或者其他。

第三,你的销毁函数( std :: function< void(T *)> 或者其他东西)。

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



这个销毁函数只是 delete t ,但是作为一个好处,它允许智能指针的用户传入一个销毁函数,它并不总是删除t



有趣的是,相当于 reset ,您替换你的销毁功能。所以你实际上可以使破坏函数的签名是 std :: function< void()> ,这使得它在 T <

  >模板< typename T> SP 
{
private:
T * pData; //指针
RC *引用; //引用计数
std :: function< void()> destroyData;
public:
模板< typename U>
SP(U * pValue):
pData(pValue),
reference(nullptr),
//存储如何现在销毁pValue,以便稍后执行:
destroyData([pValue]() - > void {
delete pValue;
})
{
//创建一个新的引用
reference = new RC() ;
//增加引用计数
reference-> AddRef();
}
//对于operator =类似,并且您可能必须为SP< T>做些事情。以及:
模板< typename U>
SP(常数SP<& sp):
pData(sp.pData),
reference(sp.reference),
destroyData(sp.destroyData)
{
//复制构造函数
//复制数据和引用指针
//并增加引用计数
reference-> AddRef();
}
模板< typename U>
SP< T>& operator =(const SP& sp)
{
//等等等等,然后
destroyData = sp.destroyData;

$ b〜SP()
{
//析构函数
//减少引用计数
//如果引用变为零删除数据
if(reference-> Release()== 0)
{
delete reference;
destroyData(); //在这里我毁了它!
}
}
};

或类似的东西


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. 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

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); where Derived is derived from Base1. Base1 has protected constructor/destructor. 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. How can I do that?

解决方案

Your smart pointer needs 3 chunks of information.

First, the pointer to the data (T* or something).

Second, your reference count: std::atomic<int> or something.

Third, your destruction function (std::function<void(T*)> or something).

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).

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.

Amusingly, on the equivalent of reset, you replace your destruction function. 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.

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

这篇关于避免对非虚拟析构函数进行对象切片的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆