弱引用
前言
#include <iostream>
#include <string>
#include <memory>
using namespace std;
class Girl;
class Boy {
public:
Boy() {
cout << "Boy 构造函数" << endl;
}
~Boy() {
cout << "~Boy 析构函数" << endl;
}
void setGirlFriend(shared_ptr<Girl> _girlFriend) {
this->girlFriend = _girlFriend;
}
private:
shared_ptr<Girl> girlFriend;
};
class Girl {
public:
Girl() {
cout << "Girl 构造函数" << endl;
}
~Girl() {
cout << "~Girl 析构函数" << endl;
}
void setBoyFriend(shared_ptr<Boy> _boyFriend) {
this->boyFriend = _boyFriend;
}
private:
shared_ptr<Boy> boyFriend;
};
void useTrap() {
shared_ptr<Boy> spBoy(new Boy());
shared_ptr<Girl> spGirl(new Girl());
// 陷阱用法
spBoy->setGirlFriend(spGirl);
spGirl->setBoyFriend(spBoy);
// 此时boy和girl的引用计数都是2
}
int main(void) {
useTrap();
system("pause");
return 0;
}
这里会发现没有调用Boy和Girl的析构函数,因为:
最后spBoy和spGirl的引用计数都会置为1,而shared_ptr和shared_ptr只有当引用计数为0时才会对所引用的指针进行析构
而对于这种情况可以释放掉内存:
首先释放spBoy,但是因为girl对象里面的智能指针还托管着boy,boy的引用计数为2,所以释放spBoy时,引用计数减1,boy的引用计数为1;
在释放spGirl,girl的引用计数减1,为零,开始释放girl的内存,因为girl里面还包含有托管boy的智能指针对象,所以也会进行boyFriend的内存释放,boy的引用计数减1,为零,接着开始释放boy的内存。最终所有的内存都释放了。
weak_ptr
概念:
weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。 同时weak_ptr 没有重载*和->但可以使用 lock 获得一个可用的 shared_ptr 对象。
弱指针的使用;
weak_ptr wpGirl_1; // 定义空的弱指针
weak_ptr wpGirl_2(spGirl); // 使用共享指针构造
wpGirl_1 = spGirl; // 允许共享指针赋值给弱指针
#include <iostream>
#include <string>
#include <memory>
using namespace std;
class Girl;
class Boy {
public:
Boy() {
cout << "Boy 构造函数" << endl;
}
~Boy() {
cout << "~Boy 析构函数" << endl;
}
void setGirlFriend(shared_ptr<Girl> _girlFriend) {
this->girlFriend = _girlFriend;
// 在必要的使用可以转换成共享指针
shared_ptr<Girl> sp_girl;
sp_girl = this->girlFriend.lock();
cout << sp_girl.use_count() << endl;
// 使用完之后,再将共享指针置NULL即可
sp_girl = NULL;
}
private:
weak_ptr<Girl> girlFriend;
};
class Girl {
public:
Girl() {
cout << "Girl 构造函数" << endl;
}
~Girl() {
cout << "~Girl 析构函数" << endl;
}
void setBoyFriend(shared_ptr<Boy> _boyFriend) {
this->boyFriend = _boyFriend;
}
private:
shared_ptr<Boy> boyFriend;
};
void useTrap() {
shared_ptr<Boy> spBoy(new Boy());
shared_ptr<Girl> spGirl(new Girl());
spBoy->setGirlFriend(spGirl);
spGirl->setBoyFriend(spBoy);
}
int main(void) {
useTrap();
system("pause");
return 0;
}
这样就可以避免“死锁”的问题
使用陷阱
-
不要让一个裸指针初始化多个 shared_ptr ;
-
不要主动删除 shared_ptr 所管理的裸指针
BigObj *p = new BigObj();
std::shared_ptr<BigObj> sp(p);
std::shared_ptr<BigObj> sp1(p);
delete p;
这里让一个裸指针初始化两个 shared_ptr ,会导致该指针被删除两次,这是错误的。另外,既然我们让智能指针管理裸指针了,就不用再自己手动去删除改指针了,否则也会导致两次删除,应该让 shared_ptr 自动去删除所管理的裸指针。
(所以使用shared_ptr的赋值都是shared_ptrA=shatred_ptrB。在创建智能指针时候都是shared_ptr ptr(new xxx),或者shared_ptr ptr.reset(new xxx),不能new创建了对象p再用shared_ptr指向p这种分步骤的)
实例:https://blog.csdn.net/zhangruijerry/article/details/100927531
3.不要在函数实参中创建shared_ptr。
例如:
function(shared_ptr<int>(new int), g()); //有缺陷
首先先要了解new一个对象的过程:
- 调用某个 operator new 分配内存
- 在该地址上构造T的对象
再了解函数参数的计算顺序:
有可能是从左到右解析形参,也有可能从右到左
所以有可能出现:先new int,然后调用g(),如果恰好g()发生异常,而shared_ptr还没有创建, 则int内存泄漏了
共享指针shared_ptr提供的默认删除程序将调用delete,而不是delete []。 这意味着仅当共享指针拥有使用new创建的单个对象时,默认删除器才适用。
std::shared_ptr<char> ptr(new char[20]); //错误,但能编译通过
因此,如果使用new []创建对象数组,则必须自定义删除器。 你可以通过传递函数,函数对象或lambda来做到这一点,后者对传递的普通指针调用delete []。 例如:
std::shared_ptr<char> ptr(new char[20],
[](char* p) {
delete[] p;
}
);
对于共享指针初始化,括号内第二个部分为用户自己定义的删除器。