一、为什么需要智能指针
C++ 的智能指针是一种用于自动管理动态内存的工具,可以避免开发者手动管理内存,减少内存泄漏和悬挂指针等问题。C++98里面引入了
auto_ptr,C++11里面引入了shared_ptr、unique_ptr和weak_ptr,并且废弃了auto_ptr。
二、std::shared_ptr
std::shared_ptr 是一种共享所有权的智能指针,允许多个 shared_ptr 指向同一个对象。它通过引用计数来管理对象的生命周期。每个 shared_ptr 都有一个内部的引用计数器。当一个新的 shared_ptr 拷贝或赋值给其他 shared_ptr 时,引用计数增加;当一个 shared_ptr 被销毁时,引用计数减少。当引用计数降为零时,对象会被自动销毁。shared_ptr 的引用计数操作是线程安全的。
- std::shared_ptr的内存模型如下:
shared_ptr 内部包含两个指针,一个指向对象,另一个指向控制块(control block),控制块中包含一个引用计数(reference count), 一个弱计数(weak count)和其它一些数据。
- std::shared_ptr的使用方法
#include <iostream>
#include <memory>
/* 常用函数方法 */
get() // 返回shared_str中保存的裸指针
reset() // 重置shared_ptr,如果带参数则将指针指向参数对象,否则置空
use_count() // 返回shared_ptr的强引用计数
unique() // 若use_count返回值是1则返回true,否则返回false
/* 初始化 */
std::shared_ptr<int> p1(new int(100));
std::shared_ptr<int> p2 = p1;
std::share_ptr<int> p3;
p3.reset(new int(200));
if (p3) {
cout << "p3 is not null." << endl;
}
std::shared_ptr<int> p4 = std::make_shared<int>(300);
/* 指定删除器
当shared_ptr管理的对象是非new对象或者没有析构函数的类可以指定删除器
删除器可以写成函数或者lambda表达式
*/
void DeleterIntPtr(int *p) {
delete p;
}
std::shared_ptr<int> p1(new int(100), DeleterIntPtr);
std::shared_ptr<int> p1(new int(100), [](int *p) {delete p;});
/* 使用过程中注意事项 */
// 1、错误,不能将原始指针直接赋值给智能指针
std::shared_ptr<int> p1 = new int(100);
// 2、管理动态数组时必须指定删除器,因为默认删除器不支持删除动态数组
std::shared_ptr<int> p2(new int[100], [](int *p) {delete [] p;});
// 3、不要用原始指针初始化多个shared_pt,因为对象会被析构多次
int *ptr = new int;
std:shared_ptr<int> p1(ptr);
std:shared_ptr<int> p2(ptr);
// 4、不要在函数实参中创建shared_ptr,因为函数参数计算顺序不同可能造成内存泄漏
function (shared_ptr<int>(new int), g()); // 可能导致int内存泄漏
// 5、不要将this指针作为shared_ptr来返回,因为this指针本身是个裸指针,可能导致类似3一样的错误
class A {
public:
shared_ptr<A> GetSelf() {
return shared_ptr<A)(this); //错误
}
~A() {
cout << "Destructor A" << endl;
}
};
std::shared_ptr<A> p1(new A);
std::shared_ptr<A> p2 = p1->GetSelf();
class A: public std::enable_shared_from_this<A>{
public:
shared_ptr<A> GetSelf() {
return shared_from_this(); //正确
}
~A() {
cout << "Destructor A" << endl;
}
};
std::shared_ptr<A> p1(new A);
std::shared_ptr<A> p2 = p1->GetSelf();
// 6、不要造成循环引用
class A {
public:
shared_ptr<B> bptr;
~A() {
cout << "Destructor A" << endl;
}
};
class B {
public:
shared_ptr<A> aptr;
~B() {
cout << "Destructor B" << endl;
}
};
std::shared_ptr<A> ap(new A);
std::shared_ptr<B> bp(new B);
ap->bptr = bp;
bp->aptr = ap;
// 这种情况下ap和bp的引用计数都为2,离开作用域后引用计数各自减1,不会调用析构函数,造成内存泄漏
三、std::weak_ptr
weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的shared_ptr, weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。
- std::weak_ptr的使用方法
#include <iostream>
#include <memory>
/* 常用函数方法 */
use_count() // 获取引用计数
exired() // 判断当前资源是否已经释放,返回bool值
lock() // 获取监视的shared_ptr,使用时相当于引用计数+1,离开作用域则-1
/* 初始化 */
shared_ptr<int> p1(new int(100));
weak_ptr<int> p2(p1);
/* 解决循环引用 */
class A {
public:
shared_ptr<B> bptr;
~A() {
cout << "Destructor A" << endl;
}
};
class B {
public:
weak_ptr<A> aptr;
~B() {
cout << "Destructor B" << endl;
}
};
std::shared_ptr<A> ap(new A);
std::shared_ptr<B> bp(new B);
ap->bptr = bp;
bp->aptr = ap;
// 只需将A或B的成员变量改为weak_ptr,这样ap的引用计数就是1,bp仍然是2,ap力扣作用域,引用计数-1为0导致A对象被析构,进而导致bptr引用计数-1为1,最后bp离开作用域引用计数继续-1为0,从而顺利被析构,无内存泄漏
四、std::unique_ptr
unique_ptr是一个独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr。它没有引用计数的开销,因此性能优越。
- std::unique_ptr的使用方法
#include <iostream>
#include <memory>
/* 初始化 */
std::unique_ptr<T> p1(new T);
auto p2 = std::make_unique<T>();
/* 使用过程中注意事项 */
// 1、不能复制给另一个unique_ptr
std::unique_ptr<T> p1(new T);
std::unique_ptr<T> p2 = p1; // 错误
// 2、可以通过std::move()函数来转让所有权
std::unique_ptr<T> p1(new T);
std::unique_ptr<T> p2 = std::move(p1); // 正确
// 3、可以指向数组
std::unique_ptr<int []> p1(new int[10]);
p1[9] = 9;
// 4、指定删除器需指定删除器类型
std::shared_ptr<int, void (*)(int *)> p1(new int(100), [](int *p) {delete p;});
五、总结
std::unique_ptr:独占所有权,不能复制,支持移动语义。
std::shared_ptr:共享所有权,引用计数管理生命周期,支持多次引用。
std::weak_ptr:不拥有对象,防止循环引用,弱引用。