3

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

我如何编写一个可以与具有受保护析构函数的对象一起使用的解决方案?

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

规范的相关位:

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

Sptr(); 构造一个指向空指针的智能指针。

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

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

4 回答 4

4

使析构函数非公开的全部目的是防止对象被任意销毁。没有什么好的方法可以解决这个问题。(即使有一个通用的方法,这也不是一个方法,因为它需要打破封装才能做到这一点。)

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

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

于 2012-11-26T19:59:12.400 回答
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可以安全地与具有非虚拟析构函数的基类一起使用。

于 2012-11-26T20:20:33.023 回答
0

您的类可以采用一个删除器函子,然后该函子将负责解除分配对象。通过这样做,您可以将访问析构函数的问题转嫁给使用您的课程的任何人。:)

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

这也将提供一种解决与放置相关的问题的方法new

于 2012-11-26T19:53:03.863 回答
0

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

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

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

编辑:这是添加自定义删除器的方式:

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

于 2012-11-26T20:13:31.197 回答