注意事项
- 永远只用
make_shared<T>
或make_unique<T>
的方式初始化和构造,因为效率高。
- 类型转换请使用:
static_pointer_cast<T>
,dynamic_pointer_cast<T>
。
循环引用
循环引用问题
class B; // 前向声明
class A {
public:
// 正确,不会增减引用计数,不造成智能指针循环引用,但是weak_ptr没有->和*,需要使用时必须先调用其lock函数返回一个shared_ptr然后才能用
// std::weak_ptr<B> m_b;
// 错误,会造成智能指针循环引用
std::shared_ptr<B> m_b;
};
class B {
public:
std::shared_ptr<A> m_a;
};
int main() {
int n = 999999;
while (n--) {
std::shared_ptr<A> a = std::make_shared<A>(); // new出来的A的引用计数此时为1
std::shared_ptr<B> b = std::make_shared<B>(); // new出来的B的引用计数此时为1
a->m_b = b; // B的引用计数增加为2
b->m_a = a; // A的引用计数增加为2
}
// b先出作用域,B的引用计数减少为1,不为0, 所以堆里的B空间没有被释放,且B持有的A也没有机会析构,A的引用计数也完全没减少
// a后出作用域,同理A的引用计数减少为1,不为0,所以堆里的A的空间也没有被释放
return system("pause");
}
解决方案
- 建模时明确所有权关系,所有者应该使用
std::shared_ptr
指向被持有者。若被持有者想要引用所有者则必须要使用std::weak_ptr
指向所有者。
删除器
绑定删除器
// shared_ptr可以绑定删除器
std::shared_ptr<int> p(new int, [](int * t) { delete t; });
改变删除器
- shared_ptr可以在运行时改变删除器,unique_ptr不可以。
std::shared_ptr<int> p(new int, [](int * t) { delete t; });
p.reset(new int, [](int * t) {
std::cout << "shared_ptr可以在运行时改变删除器" << std::endl;
delete t;
});