【C++面试高频题】C++中的四种智能指针

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;
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jasmine-Lily

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值