使用受保护的析构函数删除对象

时间:2012-11-26 19:45:30

标签: c++ c++11 shared-ptr

我必须为类编写一个共享指针,并且它必须做的许多其他事情是确保它可以删除它指向的对象。

如何编写适用于具有受保护的析构函数的对象的解决方案?

此外,如果对象是使用placement new创建的,我不应该在对象上调用delete,因为该空间可能仍在使用中(delete调用是否会起作用?) 。我怎样才能发现这种情况?

规范的相关部分:

  

void reset();智能指针设置为指向空指针。当前指向对象的引用计数(如果有)将递减。

     

特征码指针();构造一个指向空指针的智能指针。

     

template <typename U> Sptr(U *);构造一个指向给定对象的智能指针。引用计数初始化为一个。

     

Sptr(const Sptr &);
template <typename U> Sptr(const Sptr<U> &);

       引用计数递增。如果U *不可隐式转换为T *,则会导致语法错误。请注意,必须提供正常复制构造函数和成员模板复制构造函数才能正常运行。

调用代码的方式:

        Sptr<Derived> sp(new Derived);
        char *buf = (char *) ::operator new(sizeof(Sptr<Base1>));
        Sptr<Base1> &sp2 = *(new (buf) Sptr<Base1>());
        sp2 = sp;
        sp2 = sp2;
        sp.reset();
        sp2.reset();
        ::operator delete(buf);

Base1一切都受到保护。

4 个答案:

答案 0 :(得分:4)

使析构函数非公开的全部意义是防止对象被任意破坏。没有好办法解决这个问题。 (即使有一般的方法,它也不是良好的方式,因为它需要打破封装的地狱才能这样做。)

如果您希望某个对象被自身以外的某个类销毁,请将析构函数设为public。如果不这样做,那么你的指针类也将无法销毁该对象。

或者,您可以使指针类成为您希望它使用的任何类的朋友。但这在很多方面都是丑陋的,尤其是它相当于任意限制你可以使用它的有效对象类型。

答案 1 :(得分:2)

与引用计数器一起存储指向将删除对象的函数的指针('删除')。您将在智能指针的模板化构造函数中实例化删除器,您知道派生类型。这是一个非常天真的伪代码:

template<class T> void DefaultDeleter(void *p) { delete static_cast<T*>(p); }

struct ref_counter {
    int refs;
    void *p;
    void (*d)(void *);
};

template<class T> class Sptr { 
    /* ... */
    template <typename U> Sptr(U *p)
    {
        _c = new ref_counter;
        _c->refs = 1;
        _c->p = static_cast<void*>(p);
        _c->d = &DefaultDeleter<U>;
        _p = p;
    }

    T *_p;
    ref_counter *_c;
};

refs降至零时,调用(*_c->d)(_c->p)来销毁指向的对象。

我当然假设Base的析构函数受到保护,而Derived的析构函数是公开的,否则练习就没有意义了。

注意:这就是为什么std::shared_ptr可以安全地与具有非虚拟析构函数的基类一起使用。

答案 2 :(得分:0)

你的类可以使用一个删除函数,然后负责解除对象的释放。通过这样做,您可以将访问析构函数的问题转移到使用您的类的任何人身上。 :)

开玩笑说,如果调用者知道如何创建类的实例,他们也应该知道如何销毁这些实例。

这也可以解决与展示位置new相关的问题。

答案 3 :(得分:0)

在阅读您的规范更新后,我可以告诉您,没有办法正确实施,因为:

  • 您的指针无法区分新案例和展示位置新案例。您的删除者唯一可以做的就是致电delete p。这是在放置新案例中做的正确的事情尽管它可能在您的示例中有效,因为缓冲区恰好是正确的大小并且使用new分配。
  • 正如其他答案所解释的那样,你的删除器没有办法解决受保护的析构函数问题。

通过向构造函数添加自定义删除器的可能性,您可以解决这两个问题。

修改:这是添加自定义删除器的方法:

  

template <typename U> Sptr(U *, std::function<void(T*)> &&deleter);   构造一个指向给定对象的智能指针。该   引用计数初始化为一。自定义删除器被调用   当引用计数达到零时,原始指针指向   实例