使用std::shared_ptr持有非虚析构的不透明类型是否存在未定义行为
结论
你给出的代码完全符合C++标准,不存在任何未定义行为,你之前的理解全部正确。
具体原理说明
std::shared_ptr的核心特性之一就是会在构造阶段存储对应实际类型的删除器:当你在make_thing内部调用std::make_shared<Derived>()时,生成的std::shared_ptr<Derived>内部绑定的删除器会直接调用Derived类的析构函数。后续将其转换为std::shared_ptr<Base>时,删除器不会发生变化,因此对象生命周期结束销毁时,会按照Derived→Base的顺序正确调用析构函数,完全不需要依赖基类析构函数的虚函数多态分发。- 你对另外两种返回值场景的判断也完全准确:
- 若返回
std::unique_ptr<Base>:std::unique_ptr的默认删除器是绑定到基类Base的std::default_delete<Base>,该删除器需要直接调用Base的析构函数,但Base的析构是保护成员,外部类无法访问,因此代码直接编译失败。哪怕你把Base的析构改为公有,由于析构函数非虚,通过std::default_delete<Base>销毁派生类对象也会触发未定义行为。 - 若返回
Base*:用户拿到裸指针后如果手动执行delete,同样属于通过非虚析构的基类指针销毁派生类对象的场景,是C++标准明文规定的未定义行为,不会调用Derived的析构函数;而且由于Base析构是保护成员,用户甚至无法合法执行delete操作。
- 若返回
- 这段代码的设计其实非常严谨:
Base的保护析构刚好从语法层面阻止了外部直接销毁基类指针的错误用法,而工厂函数内部生成shared_ptr时,删除器是在Derived的可见作用域内生成,作为派生类有权限访问基类的保护析构,刚好适配shared_ptr的删除器机制,不存在访问权限问题。
内容的提问来源于stack exchange,提问作者Escualo
相关产品推荐
相关产品推荐

