std::unique_ptr与手动堆分配+delete的对比及优势问询
问题
假设存在基类B、子类B1和类A,需要将B1指针作为A的成员变量。可以通过手动堆分配B1并在A的析构函数中执行delete实现,示例代码如下:
class B { public: B() { }; }; class B1 : public B { public: B1() { }; }; class A { public: B* b_instance; A(B* b_instance){ this->b_instance = b_instance; }; ~A(){ delete this->b_instance; }; }; int main() { B* b_instance = new B1(); A a_instance(b_instance); return 0; };
另一种更受青睐的方式是使用std::unique_ptr:将指向B1的unique_ptr交由A管理,作为智能指针,当A销毁时,因无引用B1会自动销毁。编写的示例代码如下:
#include <memory> class B { public: B() { }; }; class B1 : public B { public: B1() { }; }; class A { public: std::unique_ptr<B> b_instance; A(B& b_instance){ this->b_instance = std::make_unique<B>(b_instance); }; ~A() { }; }; int main() { B1 b_instance; A a_instance(b_instance); return 0; };
请问:只要记得在A的析构函数中delete成员指针,两种方式是否等价?std::unique_ptr还有其他优势吗?
解答
首先得指出你的代码里的两个关键问题:
- 基类
B没有虚析构函数,直接delete基类指针指向的子类对象会触发未定义行为,这是手动管理和智能指针方式都必须修复的前提。 - 第二个
std::unique_ptr示例逻辑错误:std::make_unique<B>(b_instance)是创建了一个基类B的拷贝对象,而非指向传入的B1实例,和第一个手动指针的逻辑完全不符。正确的写法应该是让A接管B1的智能指针所有权,比如:
#include <memory> class B { public: virtual ~B() = default; // 添加虚析构 }; class B1 : public B { public: B1() { }; }; class A { public: std::unique_ptr<B> b_instance; // 通过移动语义接管unique_ptr的所有权 A(std::unique_ptr<B> ptr) : b_instance(std::move(ptr)) {}; }; int main() { std::unique_ptr<B> b_instance = std::make_unique<B1>(); A a_instance(std::move(b_instance)); return 0; };
一、两种方式是否等价?
即使修复了上述问题,手动管理指针和std::unique_ptr也不完全等价:
- 手动管理需要确保在所有代码路径(比如构造函数抛异常、对象被拷贝/赋值)中都正确执行
delete,稍有疏忽就会出现内存泄漏或重复释放;而std::unique_ptr的析构是自动触发的,不受代码分支影响。 - 手动指针无法表达独占所有权的语义,其他开发者很难直观判断这个指针的生命周期由谁负责;
std::unique_ptr则明确了它对指向对象的独占所有权,代码意图更清晰。
二、std::unique_ptr的额外优势
- 自动内存安全:不管
A的对象是正常销毁,还是因为构造函数抛异常中途退出,std::unique_ptr都会自动释放内存,彻底避免手动管理时遗漏delete导致的内存泄漏。 - 杜绝非法拷贝:
std::unique_ptr是不可拷贝的,从根源上避免了多个A对象持有同一个原始指针、进而重复释放的风险;而手动指针如果不小心被拷贝,很容易触发重复delete的崩溃。 - 语义清晰直观:
std::unique_ptr直接表达了“独占对象所有权”的意图,其他开发者一眼就能明白这个对象的生命周期由A负责,减少了沟通成本和理解偏差。 - 无需手动写析构:使用
std::unique_ptr后,A不需要手动编写析构函数,编译器会自动生成正确的析构逻辑,同时避免了手动写析构可能引入的问题(比如忘记加虚、遗漏清理逻辑)。 - 适配标准库更安全:
std::unique_ptr可以直接存入std::vector等标准容器,容器销毁时会自动清理内部的智能指针,无需手动遍历释放;而手动指针存入容器后,必须手动清理,极易出错。
内容的提问来源于stack exchange,提问作者Chris Gnam
相关产品推荐
相关产品推荐

