删除具有受保护析构函数的对象

Delete an object with a protected destructor

本文关键字:对象 析构函数 受保护 删除      更新时间:2023-10-16

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

如何编写一个解决方案来处理具有受保护的析构函数的对象?

此外,如果对象是使用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保护了一切。

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

如果希望某个对象被除其本身之外的其他类销毁,请将析构函数设为公共的。如果不这样做,那么指针类也将无法销毁对象。

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

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

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可以安全地与具有非虚拟析构函数的基类一起使用的原因。

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

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

这也将提供一种解决与放置CCD_ 12相关联的问题的方法。

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

  • 指针无法区分新大小写和放置新大小写。您的委托人唯一能做的就是调用delete p。这是而不是在放置新情况下正确的做法,尽管它在您的示例中可能会起作用,因为缓冲区恰好是正确的大小,并且分配了new
  • 正如其他答案中所解释的,对于您的删除程序来说,没有好的方法来绕过受保护的析构函数问题

通过在构造函数中添加自定义deleter的可能性,可以解决这两个问题。

编辑:这就是添加自定义删除程序的方法:

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