C++学习 智能指针专题

在C++中使用指针访问内存是一种很方便的方式,但动态内存管理容易出现两种问题

一、忘记释放内存(比如异常抛出导致直接退出函数而没执行后面的delete操作)

二、当前指针所指向的内存被释放了,该指针引用非法内存

因此为了解决这个问题,C++引入了智能指针概念,智能指针的作用是在当前代码块结束时自动释放内存

四种智能指针

auto_ptr

shared_ptr

weak_ptr

unique_ptr

此文不讨论auto_ptr(已经过时)

shared_ptr

多个shared_ptr指向同一处资源,当所有shared_ptr都全部释放时,该处资源才释放。每多一个就会计数+1,当计数为0时代表是最后一个指针便释放

#include <iostream>
#include <memory>
using namespace std;

class String {
	public:
		String(string s): ss(s) {
		}
		~String() {
			cout << "i am released" << "\n";
		}
		void show(string tmp) {
			cout << tmp << ':' << ss << '\n';
		}
	private:
		string ss;
};

int main() {
	shared_ptr <String> s (new String("hellow world"));
	shared_ptr <String> s1 (s);
	shared_ptr <String> s2 (s);

	s->show("s");
	s1->show("s1");
	s2->show("s2");

	return 0;
}

运行结果

s:hellow world
s1:hellow world
s2:hellow world
i am released

最后只被释放了一次

shared_ptr的缺陷

如果类的两个对象互相引用会出现计数不正常

#include <iostream>
#include <memory>
using namespace std;

class String {
	public:
		String(string s): ss(s) {
		}
		~String() {
			cout << "i am released" << "\n";
		}
		void show(string tmp) {
			cout << tmp << ':' << ss << '\n';
		}
		void setptr(shared_ptr <String> &tmp) {
			qq = tmp;
		}
	private:
		string ss;
		shared_ptr <String> qq;
};

int main() {
	shared_ptr <String> s1 (new String("hellow world"));
	shared_ptr <String> s2 (new String("hellow world"));

	s1->setptr(s2);
	s2->setptr(s1);
	return 0;
}

运行结果是空这代表s1和s2没有被释放

这是因为在s1中qq指向s2,s2中qq指向s1

s1计数为2,s2计数为2

当释放s1时,s1计数减一为1

当释放s2时,s2计数减一为1

但其中的qq没有被释放,所以计数没减到0两者内存都没被释放

为了解决这个问题,因引入弱指针weak_ptr概念

weak_ptr

weak_ptr通常不单独使用,一般用于查看对应的shared_ptr的信息。weak_ptr没有重载*,->等指针运算符,因此不能直接使用weak_ptr访问其指向的内存内容,其更多是作为一个观测者来使用

weak_ptr常用成员函数

reset():释放被管理对象所有权

swap():交换被管理对象

use_count():返回被管理对象的被shared_ptr指向的个数

expired():检查被管理对象是否被删除

lock():返回被管理对象的shared_ptr

注意:weak_ptr接受shared_ptr类型的变量赋值,但是反过来是行不通的,需要使用lock函数。

将上面代码的类中shared_ptr<String> qq改为weak_ptr<String> qq后输出

i am released
i am released

可见weak_ptr不会增加计数

下面是weak_ptr的简单使用

#include <iostream>
#include <memory>
using namespace std;

class String {
	public:
		String(string s): ss(s) {
		}
		~String() {
			cout << "i am released" << "\n";
		}
		void show(string tmp) {
			cout << tmp << ':' << ss << '\n';
		}
	private:
		string ss;
};

int main() {
	shared_ptr <String> s1 (new String("hellow world"));
	shared_ptr <String> s2 (s1);
	weak_ptr<String> s_monitor = s1;

	cout << "s_monitor.use_count()=" << s_monitor.use_count() << '\n';

	cout << "use s_monitor.lock():";
	s_monitor.lock()->show("s_monitor");
	return 0;
}

运行结果

s_monitor.use_count()=2
use s_monitor.lock():s_monitor:hellow world
i am released

unique_ptr

与shared_ptr不同的是unique_ptr只能一个指针指向一个内存

unique_ptr <String> s1(new String("hellow world"));
unique_ptr <String> s2;
s2 = s1;//错误

但如果s2接受的是一个临时右值又是可以的

unique_ptr <String> GetUnique_ptr(string s) {
	unique_ptr<String> tmp(new String(s));
	return tmp;
}

...
unique_ptr <String> s2 = GetUnique_ptr("hellow world");

这里 GetUnique_ptr返回了一个临时的unique_ptr<String>指针,s2接管了原来的对象,而在GetUnique_ptr函数中tmp指针被销毁(不是内存),这是合法的,但如果unique_ptr指针要存在一段时间就不可以这样做,如果非要这么做并保留原来的unique_ptr指针,应该使用move函数更加安全

unique_ptr <String> s1(new String("hellow world"));
unique_ptr <String> s2 = move(s1);
s1 = unique_ptr <String> (new String("hellow world"));

其他的一些琐碎的知识

一、所有智能指针应该避免使用非堆内存

String s("hellow world");
unique_ptr <String> s1(&s);

s1过期时就会释放s存在栈中的内容,这是错误的

二、尽量不要将普通指针和智能指针混用,如果两者指向同一块内存就会释放多遍内存

三、unique_ptr可以用数组,而shared_ptr不可以

例如unique_ptr<int []>s(new int(10))//自动使用delete[]

四、在unique_ptr作为右值时可以赋给shared_ptr,shared_ptr将接管unique_ptr所有对象

unique_ptr <String> GetUnique_ptr(string s) {
	unique_ptr<String> tmp(new String(s));
	return tmp;
}
...
unique_ptr <String> s1(new String("hellow world"));
shared_ptr <String> s2(s1)//错误的,s1为左值
shared_ptr <String> s2(GetUnique_ptr("test!"));//合法的。因为函数返回的是unique_ptr右值

猜你喜欢

转载自blog.csdn.net/qq_30798083/article/details/127421245