现代C++——智能指针

发布时间:2026/8/6 6:58:58
现代C++——智能指针 1. std::shared_ptrstd::shared_ptr 是一种智能指针它能够记录多少个 shared_ptr 共同指向一个对象。delete当引用计数变为零的时候就会将对象自动删除。使用 std::shared_ptr 仍然需要使用 new 来调用这使得代码出现了某种程度上的不对称。std::make_shared 就能够用来消除显式的使用 new所以 std::make_shared 会分配创建传入参数中的对象并返回这个对象类型的 std::shared_ptr指针。例如#include iostream #include memory void foo(std::shared_ptrint i) { (*i); } int main() { auto pointer std::make_sharedint(10); foo(pointer); std::cout *pointer std::endl; // 结果是11 return 0; //出了作用范围后自动销毁 }std::shared_ptr 可以通过 get() 方法来获取原始指针通过 reset() 来减少一个引用计数并通过 use_count() 来查看一个对象的引用计数。例如auto pointer std::make_sharedint(10); auto pointer2 pointer; // 引用计数1 auto pointer3 pointer; // 引用计数1 int *p pointer.get(); // 这样不会增加引用计数 std::cout pointer.use_count() pointer.use_count() std::endl; // 3 std::cout pointer2.use_count() pointer2.use_count() std::endl; // 3 std::cout pointer3.use_count() pointer3.use_count() std::endl; // 3 pointer2.reset(); std::cout reset pointer2: std::endl; std::cout pointer.use_count() pointer.use_count() std::endl; // 2 std::cout pointer2.use_count() pointer2.use_count() std::endl; // pointer2 已 reset; 0 std::cout pointer3.use_count() pointer3.use_count() std::endl; // 2 pointer3.reset(); std::cout reset pointer3: std::endl; std::cout pointer.use_count() pointer.use_count() std::endl; // 1 std::cout pointer2.use_count() pointer2.use_count() std::endl; // 0 std::cout pointer3.use_count() pointer3.use_count() std::endl; // pointer3 已 reset; 0当使用 shared_ptr 的时候每添加一个指向同一个对象的指针就会增加一个计数这个计数是总计数所以在打印的第一部分会发现调用三个指针分别查看这个对象的引用数都是 3 。根据不同的指针使用 reset() 只会消除对应指针对这块内存的所有权第二处打印前对 p2 进行了减少计数的操作此时分别打印会发现 p1 和 p3 都是 2 而 p2 变成了 0。这是因为每个 shared_ptr 在添加的时候把一个数公共的加进了计数中所以 reset() 操作把自己对这块内存的所有权放弃后总计数就会减 1 。但是既然已经放弃了所有权所以 p2 就无法正常显示刚才那块内存的计数所以就显示 0。第三段打印时也是相同的原因。2. std::unique_ptrstd::unique_ptr 是一种独占的智能指针它禁止其他智能指针与其共享同一个对象从而保证代码的安全std::unique_ptrint pointer std::make_uniqueint(10); // make_unique 从 C14 引入 std::unique_ptrint pointer2 pointer; // 非法既然是独占那就是不可复制。但是我们可以利用 std::move 将其转移给其他的 unique_ptr例如#include iostream #include memory struct Foo { Foo() { std::cout Foo::Foo std::endl; } ~Foo() { std::cout Foo::~Foo std::endl; } void foo() { std::cout Foo::foo std::endl; } }; void f(const Foo ) { std::cout f(const Foo) std::endl; } int main() { //创造一个类型为 unique_ptrFoo 的智能指针 p1 初始化通过Foo的构造函数 std::unique_ptrFoo p1(std::make_uniqueFoo()); // p1 不空, 输出 if (p1) p1-foo(); { //通过 move 将 p1 转移给 p2 完成 p2 的初始化此时 p1 失去了这块内存的访问权 std::unique_ptrFoo p2(std::move(p1)); // p2 不空, 输出 f(*p2); // p2 不空, 输出 if(p2) p2-foo(); // p1 为空, 无输出 if(p1) p1-foo(); //再通过 move 把 p2 的所有权转给 p1 p1 std::move(p2); // p2 为空, 无输出 if(p2) p2-foo(); std::cout p2 被销毁 std::endl; } // p1 不空, 输出 if (p1) p1-foo(); // Foo 的实例会在离开作用域时被销毁 }3. std::weak_ptr如果你仔细思考 std::shared_ptr 就会发现依然存在着资源无法释放的问题。看下面这个例子class Child; class Parent { public: std::shared_ptrChild child; // 爸爸持有儿子的 shared_ptr ~Parent() { std::cout 爸爸走了\n; } }; class Child { public: std::shared_ptrParent parent; // 儿子持有爸爸的 shared_ptr ~Child() { std::cout 儿子走了\n; } }; int main() { auto dad std::make_sharedParent(); auto son std::make_sharedChild(); dad-child son; // 爸爸指向儿子计数儿子对象 2 (son dad-child) son-parent dad; // 儿子指向爸爸计数爸爸对象 2 (dad son-parent) // main 结束dad 和 son 销毁计数各自减为 1 // 内存泄漏两个对象互相抱着无法释放 }爸爸要等儿子走才走儿子要等爸爸走才走结果谁也走不了导致内存泄漏。解决这个问题的办法就是使用弱引用指针 std::weak_ptrstd::weak_ptr 是一种弱引用相比较而言 std::shared_ptr 就是一种强引用。class Child { public: std::weak_ptrParent parent; // 改成弱引用儿子知道有爸爸但不“拽着”爸爸不让走 ~Child() { std::cout 儿子走了\n; } };std::weak_ptr 没有 * 运算符和 - 运算符所以不能够对资源进行操作它可以用于检查std::shared_ptr 是否存在。使用 expired() 方法能在对象资源未被释放时返回 false否则返回 true 。除此之外还可以升级成强引用通过 lock() 可以升级成临时的 shared_ptr。