1 C++中的四种智能指针
1.1 目的
- 内存资源被释放,指针指向没有改变,产生野指针。
- 内存资源被释放,又试图再释放一次。
- 申请的空间在函数结束时忘记释放,导致的内存泄露问题。
1.2 定义
智能指针是一种类模板,当对象生命周期结束时,会自动调用其析构函数,释放内存。
1.2.1 常用接口
常用接口:T是模板参数。
//用于的到C++原生指针,脱离了智能指针的管理,比较危险
T* get();
//*解引用运算符重载,就是获取到指针指向的值
T& operator*();
//->间接成员运算符重载
T* operator->();
//=运算符重载
T& operator=(const T& val);
//将智能指针指向nullptr,但是未破坏指针所指向的内容,函数返回值为智能指针置nullptr之前的值
T* release();
//释放智能指针所指向的内存,并将智能指针置为ptr,默认值为nullptr;原来指向的内存将被释放掉
void reset (T* ptr = nullptr)
1.2.2 注意事项
对于智能指针,如果想手动释放其所指向的内存,就将智能指针赋值为nullptr,系统将会自动释放智能指针指向的内存。
1.3 类型
1.3.1 auto_ptr
所有权是可以被剥夺的。但是存在内存崩溃的问题,如下图所示,执行到cout << *p1 << endl;这一句时,程序会崩溃。auto_ptr在C++11中已经被抛弃。
不支持对象数组的内存管理。
cout << "auto_ptr*************************************************" << endl;
auto_ptr<string> a1(new string("Hello World!"));
auto_ptr<string> a2 = a1;
cout << *a2 << endl;
//cout << *p1 << endl;//此处会报错,存在内存崩溃的问题
1.3.2 unique_ptr
采用所有权的方式,同一时间只有一个智能指针指向对象。不支持拷贝,如果一定要转让所有权,就要使用move()函数。
cout << "unique_ptr **********************************************" << endl;
//支持指向单个对象
unique_ptr<string> u1(new string("Hello World!"));
//unique_ptr<string> u2 = u1;//报错:尝试引用已删除的函数
unique_ptr<string> u2 = move(u1);
cout << *u2 << endl;
//cout << *u1 << endl;//此时u1指向了nullptr,解引用会报错
//支持指向数组
unique_ptr<int[]> u3(new int[10]{0});
//输出:0 0 0 0 0 0 0 0 0 0
for (int i = 0; i < 10; i++)
{
cout << u3[i] << " ";
}
cout << endl;
make_unique初始化
make_unique是一个泛型,<>里面为数据类型,()对应着new()里的东西,其返回值是一个unique_ptr类型的变量。
语句:
auto sp1=make_unique<int>(100);(相当于make_unique<int> sp1(new int(100));)
1.3.3 shared_ptr
可以有多个shared_ptr指向同一内存地址,使用计数的方式管理,如A引用了一个内存对象,此时引用计数为1。B引用了同一个内存对象,此时引用计数为2。C也引用了同一个内存对象,此时引用计数为3。此时B不使用内存了,释放了shared_ptr,此时引用计数变为2。接着A也不使用了,释放了shared_ptr,此时引用计数变为1。最后C也不使用了,释放了shared_ptr,此时引用计数变为0,当引用计数为0之后,释放内存对象占用的内存。
相关函数:可以通过成员函数 use_count() 来查看资源的所有者个数,除了可以通过 new 来构造,还可以通过传⼊auto_ptr,unique_ptr,weak_ptr 来构造。当我们调⽤ release() 时,当前指针会释放资源所有权,计数减⼀。
cout << "shared_ptr**********************************************" << endl;
//支持指向单个对象
shared_ptr<string> s1(new string("Hello World!"));
shared_ptr<string> s2 = s1;
cout << *s1 << endl;//Hello World!
cout << *s2 << endl;//Hello World!
cout << s1.use_count() << endl;//2
//支持指向数组 C++17后支持
shared_ptr<int[]> s3(new int[10]{ 0 });
//输出:0 0 0 0 0 0 0 0 0 0
for (int i = 0; i < 10; i++)
{
cout << s3[i] << " ";
}
cout << endl;
make_shared初始化
make_shared是一个泛型,<>里面为数据类型,()对应着new()里的东西,其返回值是一个shared_ptr类型的变量。
语句:
auto sp1=make_shared<int>(100);(相当于shared_ptr<int> sp1(new int(100));)
自定义删除器
bool del(int *p){
delete [] p;
}
shared_ptr<int> shared(new int[100],del);//使用函数
shared_ptr<int> ptr(new int[100],[](int *p){delete [] p;});//使用lambda表达式
线程安全问题
引用计数和指向变量的指针作为一种临界资源,如果不加锁,那么在多线程情况下会出现线程安全问题。
循环引用问题
两个对象相互使用shared_ptr成员变量指向对方造成循环引用,导致引用计数失效。这个会导致内存泄露的问题,违背了智能指针的初衷。
# include<iostream>
using namespace std;
class father;
class son;
class father
{
public:
shared_ptr<son> son;
public:
~father() //析构函数
{
cout << "father distructor" << endl; //删除对象
}
};
class son
{
public:
shared_ptr<father> father;
public:
~son()
{
cout << "son distructor" << endl; //删除对象
}
};
//传入的是父亲和儿子的指针的指针
void test(father** f, son** s)
{
//一个父亲的智能指针指向一个父亲类
shared_ptr<father> pfath(new father());
//一个儿子的智能指针指向一个儿子类
shared_ptr<son> pson(new son());
//父亲的普通指针指向 智能指针指向的内容
*f = pfath.get();
//儿子的普通指针指向 智能指针指向的内容
*s = pson.get();
//父亲智能指针的儿子指向 儿子智能指针指向的内存
pfath->son = pson;//儿子智能指针 count++
//儿子智能指针的父亲指向 父亲智能指针指向的内存
pson->father = pfath;//父亲智能指针 count++
cout << "pfath use_count:" << pfath.use_count() << endl; // father对象usecount:2
cout << "pson use_count:" << pson.use_count() << endl; // sond对象usecount:2
}
int main()
{
//循环引用的问题
father* pfather;//这是一个父亲的普通指针
son* pson;//这是一个儿子的普通指针
test(&pfather, &pson); // test()结束以后析构函数并没有被调用(因为count还没有为0)
cout << "pfath use_count:" << pson->father.use_count() << endl; // father对象usecount:1
cout << "pson use_count:" << pfather->son.use_count() << endl; // sond对象usecount:2
return 0;
}
1.3.4 weak_ptr
weak_ptr 是⼀种不控制对象⽣命周期的智能指针,它指向⼀个 shared_ptr 管理的对象。进⾏该对象的内存管理
的是那个强引⽤的 shared_ptr。
weak_ptr 只是提供了对管理对象的⼀个访问⼿段。weak_ptr 设计的⽬的是为配合 shared_ptr ⽽引⼊的⼀种智
能指针来协助 shared_ptr ⼯作,它只可以从⼀个 shared_ptr 或另⼀个 weak_ptr 对象构造,,它的构造和析构不会
引起引⽤记数的增加或减少。weak_ptr 没有重载*和->但可以使用 lock 获得一个可用的 shared_ptr 对象。
cout << "weak_ptr**********************************************" << endl;
shared_ptr<string> s1(new string("Hello World!"));
// 弱指针的使用
weak_ptr<string> w1; // 定义空的弱指针
weak_ptr<string> w2(s1); // 使用共享指针构造
w1 = s1; // 允许共享指针赋值给弱指针
//可以调用use_count()成员函数
cout << "w1 \t use_count = " << w1.use_count() << endl;
cout << "w2 \t use_count = " << w2.use_count() << endl;
// 不支持 * 和 -> 对指针的访问
// 在必要的使用可以转换成共享指针
shared_ptr<string> s4;
s4 = w1.lock();
cout << s4.use_count() << endl;
// 使用完之后,再将共享指针置NULL即可
s4 = NULL;