[STL 系列]智能指针:在父类没有虚析构函数的情况下,通过父类指针析构子类对象

1、现象

#include <memory>
#include <iostream>
using namespace std;

class A
{
public:
    ~A() { cout << "A" << endl; }
};

class B : public A
{
public:
    ~B() { cout << "B" << endl; }
};

int main()
{
    shared_ptr<A> ptr(new B);

    return 0;
}

为什么输出结果是
B
A
在父类没有虚析构函数的情况下,通过父类指针析构子类对象。为什么智能指针可以做到这个

2、实现

std::shared_ptr 在创建时会保存删除器(deleter),该删除器在 std::shared_ptr 被销毁时调用。默认情况下,std::shared_ptr 使用 delete 操作符作为删除器。因此,当你创建 std::shared_ptr 对象时,实际上会保存一个指向派生类对象的指针,并在销毁时正确地调用派生类的析构函数。

template <typename T>
class MySharedV2
{
public:
    template <typename T2>
    MySharedV2(T2 *p)
    {
        data_ = p;
        deleter_ = [p](){ delete p;};
    }
    ~MySharedV2()
    {
        deleter_();
    }
    T* operator->()
    {
        return data_;
    }
private:
    std::function<void()> deleter_;
    T* data_ = nullptr;
};

3、STL源代码分析

  • 1、构造
    在 shared_ptr 的构造过程中,模板构造函数会接受一个指向派生类对象的原始指针,并将其传递给内部的 _M_ptr 成员,同时构造一个 _M_refcount 对象以管理引用计数和删除器。
      template<typename _Yp, typename = _Constructible<_Yp*>>
	explicit
	shared_ptr(_Yp* __p) : __shared_ptr<_Tp>(__p) { }
  • 2、内部实现
    __shared_ptr 类的构造函数会初始化指针和引用计数对象,并为指针类型设置删除器。
      template<typename _Yp, typename = _SafeConv<_Yp>>
	explicit
	__shared_ptr(_Yp* __p)
	: _M_ptr(__p), _M_refcount(__p, typename is_array<_Tp>::type())
	{
	  static_assert( !is_void<_Yp>::value, "incomplete type" );
	  static_assert( sizeof(_Yp) > 0, "incomplete type" );
	  _M_enable_shared_from_this_with(__p);
	}

在这段代码中:

_M_ptr 被初始化为指向传入的对象指针。
_M_refcount 被初始化为一个包含指针和删除器的对象。
static_assert 确保传入的指针类型是完整类型,不能是 void。

template<typename _Ptr, _Lock_policy _Lp>
class _Sp_counted_ptr final : public _Sp_counted_base<_Lp>
{
public:
    explicit
    _Sp_counted_ptr(_Ptr __p) noexcept
    : _M_ptr(__p) { }

    virtual void
    _M_dispose() noexcept
    { delete _M_ptr; }

private:
    _Ptr _M_ptr;
};

当 delete _M_ptr 被调用时,_M_ptr 的实际类型是 B*,即使 _M_ptr 被存储为 A* 类型的指针。这是因为 std::shared_ptr 是模板类,它在实例化时知道实际的类型(即 B*),并将这个类型传递给内部的删除器。
模板实例化的行为
具体来说,在模板实例化过程中,shared_ptr 知道传入的是 B* 类型的指针。虽然存储的是 A* 类型的指针,但是当 delete 操作符被调用时,C++ 会确保调用的是指针实际类型的析构函数(即 B 的析构函数)。然后,B 的析构函数会自动调用 A 的析构函数。这是C++对象模型的一部分,保证了对象的完整销毁过程。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值