当第二个示例只调用基类中的析构函数时,为什么在使用std :: shared_ptr deallocation从基类和派生类调用析构函数?
class Base
{
public:
~Base()
{
std::cout << "Base destructor" << std::endl;
}
};
class Derived : public Base
{
public:
~Derived()
{
std::cout << "Derived destructor" << std::endl;
}
};
void virtual_destructor()
{
{
std::cout << "--------------------" << std::endl;
std::shared_ptr<Base> sharedA(new Derived);
}
std::cout << "--------------------" << std::endl;
Base * a = new Derived;
delete a;
}
Run Code Online (Sandbox Code Playgroud)
输出:
--------------------
Derived destructor
Base destructor
--------------------
Base destructor
Run Code Online (Sandbox Code Playgroud)
在这两种情况下我都期待相同的行为.
Ker*_* SB 16
delete a是未定义的行为,因为该类Base没有虚拟析构函数,并且*a(更准确地说:包含最多派生的对象*a)的"完整对象" 不是类型Base.
共享指针是使用推导的删除器删除a创建的Derived *,因此一切都很好.
(演绎的删除者的效果就是说delete static_cast<Derived*>(__the_pointer)).
如果要使用共享指针重现未定义的行为,则必须立即转换指针:
// THIS IS AN ERROR
std::shared_ptr<Base> shared(static_cast<Base*>(new Derived));
Run Code Online (Sandbox Code Playgroud)
从某种意义上说,共享指针的行为方式是正确的:因为你已经为类型擦除的删除器和分配器付出虚拟查找的代价,所以你不必支付也是公平的用于析构函数的另一个虚拟查找.类型擦除的删除器记住完整类型,因此不会产生进一步的开销.
Kerrek SB答案的一个缺失是如何shared_ptr知道类型?
答案是涉及3种类型:
shared_ptr<Base>)并且shared_ptr 不知道实际的动态类型,但知道哪个静态类型被传递给它的构造函数.然后它会练习类型擦除...但是记得某种类型.一个示例实现是(不共享):
template <typename T>
class simple_ptr_internal_interface {
public:
virtual T* get() = 0;
virtual void destruct() = 0;
}; // class simple_ptr_internal_interface
template <typename T, typename D>
class simple_ptr_internal: public simple_ptr_internal_interface {
public:
simple_ptr_internal(T* p, D d): pointer(p), deleter(std::move(d)) {}
virtual T* get() override { return pointer; }
virtual void destruct() override { deleter(pointer); }
private:
T* pointer;
D deleter;
}; // class simple_ptr_internal
template <typename T>
class simple_ptr {
template <typename U>
struct DefaultDeleter {
void operator()(T* t) { delete static_cast<U*>(t); }
};
template <typename Derived>
using DefaultInternal = simple_ptr_internal<T, DefaultDeleter<Derived>>;
public:
template <typename Derived>
simple_ptr(Derived* d): internal(new DefaultInternal<Derived>{d}) {}
~simple_ptr() { this->destruct(); }
private:
void destruct() { internal->destruct(); }
simple_ptr_internal_interface* internal;
}; // class simple_ptr
Run Code Online (Sandbox Code Playgroud)
请注意,由于这种机制,shared_ptr<void>实际上是有意义的,可以用来携带任何数据,妥善处理它.
还要注意,这种语义涉及到一个惩罚:需要对属性进行类型擦除所需的间接寻址deleter.