我必须为类编写一个共享指针,并且它必须做的许多其他事情是确保它可以删除它指向的对象。
如何编写适用于具有受保护的析构函数的对象的解决方案?
此外,如果对象是使用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
一切都受到保护。
答案 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);
构造一个指向给定对象的智能指针。该 引用计数初始化为一。自定义删除器被调用 当引用计数达到零时,原始指针指向 实例