假设我们有一个base
类和一个derived
。所以:
class base {
protected:
~base(){
//...
}
// ...
};
class derived : public base {
// ...
};
现在假设我们使用上面的类和一个智能指针类来编写代码:
SmartPointer<base> bptr(new derived());
delete bptr;
我知道它会通过调用derived
的析构函数来防止derived
对象的切片,但是它怎么知道要这样做呢?难道存储在智能指针中的引用不是base*
类型的引用吗?它是否遍历某种层次结构树,将指针转换为derived*
,然后调用delete?还是有什么我不知道的事?
实现应该是线程安全的,非侵入性的,引用计数的。
YES,您看到的类与我正在测试的类相似。显然有一种方法可以用这些给定的类来做到这一点。关于如何实现的主要想法在我上面的问题中提到了,但我不确定这样的实现如何工作。
第一件事就是代码不能工作。base
的析构函数必须至少是protected
(或者派生类是基类的友类)。private
析构函数意味着编译器不允许您为派生类编写析构函数。现在,假设你有一个protected
析构函数…(记住,如果你设计一个要扩展的类,要么提供一个公共虚析构函数,要么提供一个受保护的非虚的)
一切都取决于SmartPointer
的实现,特别是std::shared_ptr
(或boost对应的boost::shared_ptr
)能够干净地管理这种情况。该解决方案执行某种类型的部分类型擦除以实现销毁目的。基本上,智能指针有一个模板化的构造函数,它接受任何可以赋值给base
指针的指针,但是因为它是模板化的,所以它知道具体的类型。此时,它存储一个合成的deleter
函数,该函数将调用相应的析构函数。
std::function
:
template <typename T>
void delete_deleter( void * p ) {
delete static_cast<T*>(p);
}
template <typename T>
class shared_pointer {
T * ptr;
std::function<void(void*)> deleter;
public:
template <typename U>
shared_pointer( U* p, std::function<void()> d = delete_deleter<U> )
: ptr(p), deleter(d)
{}
~shared_pointer() {
deleter( ptr ); // call the stored destructor
}
};
该代码仅用于展示,它将不得不调整生产(在哪里存储function
,引用计数…),但它足以给你的想法:在唯一知道对象的确切类型的函数中(当创建智能指针时),您创建一个包装器,它将调用您需要的析构函数的确切版本(提供一些类型擦除),然后将其保留在那里,当您需要delete
时,对象调用它而不是delete
操作符。
这也可以用来管理其他需要调用特殊方法而不是delete
的资源:
// exhibition only!
shared_pointer<Foo> p( Factory.create(), &Factory::release );
在制作这个产品之前还有很多工作要做。
对std::function
的依赖用于简化擦除,可以从问题中消除。在简单的情况下(在智能指针中只支持用new
分配内存和用delete
释放内存),然后只提供一个deleter
基类和一个虚拟operator()(void*)
,然后将现有的delete_deleter
重构为从deleter
模板化的派生类,这些类用当前实现覆盖operator()(void*)
。如果你需要一般情况下(保存任何类型的资源),这是不值得的努力,只使用std::function
或boost::function
.
首先,你的析构函数不应该是私有的,否则根本不会编译。其次,如果你使用"智能指针",你可能根本不应该手动删除指针(我不知道你使用的是什么实现,但这对我来说很奇怪)。
无论如何,如果你想知道当通过指向基类的指针删除对象时如何调用派生类的析构函数,答案是多态性。但是你的析构函数中缺少virtual
声明,现在你的代码不会调用派生类的析构函数。
大多数c++实现是通过虚表实现的。
如果您使用任何boost智能指针或其他不是Base
类的friend
的指针,则此代码将无法编译,因为Base
类的析构函数是protected
(与private
相同,用于其他独立于Base
类)。
现在让我们考虑你让SmartPointer<Base>
和Base
成为朋友。这种情况下,代码将工作,但它不会调用Derived
的destructor,而是Base
的destructor,因为这里您的Base
类不是多态的。您应该将Base
的destrucotr声明为virtual
。在最后一种情况下,正确的析构函数将在删除智能指针时被调用。
此程序无效。
1)基数的大夫是私有的
来回答你的问题:你需要纠正#1和#2。则将使用动态调度(它将以相反的构造顺序调用每个dr)调用dr。
不做这些修正,SmartPointer
在这个例子中知道调用派生的医生的唯一方法是,如果SmartPointer
过于聪明(或者使用起来乏味)。
基类的析构函数需要是虚的,以确保在通过基指针删除时调用派生类的析构函数。
维基百科关于虚函数的条目