C++智能指针(3/3)

目录

上一节内容

share_ptr用法

share_ptr指针可以用于上一节所说的错误

 例子(类定义)

主函数代码

执行的结果

解释说明

share_ptr 相关构造

空的share指针可以指向其他相同类型的变量来进行托管

可以shared_ptr< T > sp2(new T())也可以shared_ptr< T > sp2(sp1)

shared_ptr sp4; 空的shared_ptr,指向类型为T[]的数组对象(C++17之后支持)[]>

shared_ptr sp5(new T[] { … }); 指向类型为T的数组对象(C++17之后支持)[]>

shared_ptr< T > sp6(NULL, D()); //空的shared_ptr,接受一个D类型的删除器,使用D释放内存

shared_ptr< T > sp7(new T(), D()); //定义shared_ptr,指向类型为T的对象,接受一个D类型的删除器,使用D删除器来释放内存

share_ptr初始化

share_ptr的构造函数

使用make_shared 初始化对象,分配内存效率更高(推荐使用)

赋值

 主动释放

重置指针

交换指针的值

share_ptr使用陷阱

原因

解决办法之一

weak_ptr智能指针(弱指针)

weak智能指针的用法

注意

弱指针到共享指针的转化

特别感谢


上一节内容

C++智能指针(2/3)_木木em哈哈的博客-CSDN博客自动释放内存:智能指针使用了RAII(资源获取即初始化)的原则,在创建时分配内存,在销毁时自动释放内存,无需手动管理内存释放,避免了因为忘记释放内存而造成的内存泄漏问题。unique_ptr是一种独占所有权的智能指针,同一时间只能有一个unique_ptr指向一个对象,当unique_ptr被销毁时,对象也会被释放。在这之中开始时p1托管str的指针,后面p2接管str指针的同时会把p1的托管给取消,这样p1指针指向的就是NULL(空),从而报错。这是因为auto_ptr与unique_ptr的排他性。https://blog.csdn.net/mumuemhaha/article/details/131689322?spm=1001.2014.3001.5502这一节我们来学最后的share_ptr和weak_ptr

share_ptr用法

shared_ptr使用引用计数的方式来管理资源,即每个shared_ptr对象都有一个关联的计数器,记录有多少个shared_ptr对象共享同一块内存资源。当计数器为0时,资源会被自动释放。

当复制或拷贝时,引用计数加1,当智能指针析构时,引用计数减1,如果计数为零,代表已经没有指针指向这块内存,那么我们就释放它!这就是 shared_ptr 采用的策略!

share_ptr指针可以用于上一节所说的错误

 例子(类定义)

class Person {
public:
	Person(int v) {
		this->no = v;
		cout << "构造函数 \t no = " << this->no << endl;
	}

	~Person() {
		cout << "析构函数 \t no = " << this->no << endl;
	}

private:
	int no;
};

主函数代码

int main()
{
    //定义两个share指针同时第二个由于已经构造好了,引用次数+1
	shared_ptr<Person> sp1;

	shared_ptr<Person> sp2(new Person(2));

	// 获取智能指针管控的共享指针的数量	use_count():引用计数
	cout << "sp1引用计数 = " << sp1.use_count() << endl;
	cout << "sp2引用计数 = " << sp2.use_count() << endl << endl;

	// 共享同时也共享引用次数
	sp1 = sp2;
    //打印出来引用次数同时验证猜想
	cout << "sp1引用计数 = " << sp1.use_count() << endl;
	cout << "sp2引用计数 = " << sp2.use_count() << endl << endl;
    
	shared_ptr<Person> sp3(sp1);
	cout << "sp1引用计数 = " << sp1.use_count() << endl;
	cout << "sp2引用计数 = " << sp2.use_count() << endl;
	cout << "sp2引用计数 = " << sp3.use_count() << endl << endl;

	return 0;
}

执行的结果

构造函数         no = 2
sp1引用计数 = 0
sp2引用计数 = 1

sp1引用计数 = 2
sp2引用计数 = 2

sp1引用计数 = 3
sp2引用计数 = 3
sp2引用计数 = 3

析构函数         no = 2

D:\C++\调试\x64\Debug\调试.exe (进程 1536)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

解释说明

注释中说的很明白了,只要引用了share_ptr所指向的变量,他的引用计数就会加一

同时在函数中sp1,sp2,sp3共同管理一个share_ptr指针

share_ptr 相关构造

空的share指针可以指向其他相同类型的变量来进行托管

shared_ptr<Person> sp1;
Person *person_1 = new Person(1);
sp1.reset(person_1);	// 托管person1

可以shared_ptr< T > sp2(new T())也可以shared_ptr< T > sp2(sp1)

shared_ptr<Person> sp2(new Person(2));
shared_ptr<Person> sp3(sp1);

shared_ptr<T[]> sp4; 空的shared_ptr,指向类型为T[]的数组对象(C++17之后支持)

shared_ptr<Person[]> sp4;

shared_ptr<T[]> sp5(new T[] { … }); 指向类型为T的数组对象(C++17之后支持)

shared_ptr<Person[]> sp5(new Person[5] { 1, 2, 3, 4, 5 });

创建一个有五个Person元素的数组分别为1,2,3,4,5也就是Person(1),Person(2)......

shared_ptr< T > sp6(NULL, D()); //空的shared_ptr,接受一个D类型的删除器,使用D释放内存

假设删除函数为

class Person {
public:
	Person(int v) {
		this->no = v;
		cout << "构造函数 \t no = " << this->no << endl;
	}

	~Person() {
		cout << "析构函数 \t no = " << this->no << endl;
	}

private:
	int no;
};

// 仿函数,内存删除
class DestructPerson {
public:
	void operator() (Person *pt) {
		cout << "DestructPerson..." << endl;
		delete pt;
	}
};

则用法为

shared_ptr<Person> sp6(NULL, DestructPerson());

shared_ptr< T > sp7(new T(), D()); //定义shared_ptr,指向类型为T的对象,接受一个D类型的删除器,使用D删除器来释放内存

shared_ptr<Person> sp7(new Person(8), DestructPerson());

share_ptr初始化

share_ptr的构造函数

shared_ptr<Person> up1(new Person(10));  // Person(10) 的引用计数为1
shared_ptr<Person> up2(up1);  // 使用智能指针up1构造up2, 此时Person(10) 引用计数为2

使用make_shared 初始化对象,分配内存效率更高(推荐使用)

shared_ptr<Preson> up3 = make_shared<Preson>(2); // 多个参数以逗号','隔开,最多接受十个

赋值

shared_ptrr<int> up1(new int(10));  // int(10) 的引用计数为1
shared_ptr<int> up2(new int(11));   // int(11) 的引用计数为1
up1 = up2;	// 因为为赋值,故int(10) 的引用计数减1,计数归零内存释放,up2共享int(11)给up1, int(11)的引用计数为2

 主动释放

shared_ptrr<int> up1(new int(10));
up1 = nullptr ;	// int(10) 的引用计数减1,计数归零内存释放 
// 或
up1 = NULL; // 作用同上 

重置指针

p.reset() ; 将p重置为空指针,所管理对象引用计数 减1
p.reset(p1); 将p重置为p1(的值),p 管控的对象计数减1,p接管对p1指针的管控
p.reset(p1,d); 将p重置为p1(的值),p 管控的对象计数减1并使用d作为删除器
p1是一个指针!

交换指针的值

std::swap(p1,p2); // 交换p1 和p2 管理的对象,原对象的引用计数不变
p1.swap(p2);    // 交换p1 和p2 管理的对象,原对象的引用计数不变

share_ptr使用陷阱

小心因循环引用造成无法释放资源!

如下代码:
Boy类中有Girl的智能指针;
Girl类中有Boy的智能指针;
当他们交叉互相持有对方的管理对象时…(借用大佬写的代码)

#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的指针,而girl里也有个boy的指针

到最后

Boy 构造函数
Girl 构造函数
请按任意键继续. . .

空间都没有释放出来(没有析构函数)

原因

 这里由于都各有对方的类,故引用计数为2

但是当程序结束的时候

 函数中的智能指针清理掉了,但是类中嵌套的智能指针无法释放,所有类无法调用析构函数

解决办法之一

如果不用两个类中的指针互相关联,而是一方单方面获得管理对方的共享指针,这样可以正常释放

假设还是上面的那个例子

由于因为开始释放boy是引用次数为2,函数结束计数减一变为一

之后开始释放girl,girl引用次数为1,函数结束计数减一变为零

这是开始析构girl,同时就会把boy类也析构掉,计数再次减一

这是boy引用次数为零,则boy也开始析构了

void useTrap() {
	shared_ptr<Boy> spBoy(new Boy());
	shared_ptr<Girl> spGirl(new Girl());

	// 单方获得管理
	//spBoy->setGirlFriend(spGirl);
	spGirl->setBoyFriend(spBoy);	
}

weak_ptr智能指针(弱指针)

weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。 同时weak_ptr 没有重载*和->但可以使用 lock 获得一个可用的 shared_ptr 对象。

weak智能指针的用法

weak_ptr的定义空指针(weak_ptr <T>  a)和共享构造(weak_ptr <T> a(b)以及允许共享指针赋值给弱指针(a=b)都和其他智能指针差不多

同时和share_ptr一样可以用 .count()来查看引用次数

注意

弱指针不支持 * 和 -> 对指针的访问

弱指针到共享指针的转化

弱指针到共享指针的转化可以用.lock()来表示

例如

shared_ptr<Girl> sp_girl;
sp_girl = wpGirl_1.lock();

// 使用完之后,再将共享指针置NULL即可
sp_girl = NULL;

上面share_ptr发生的问题的另一种解决办法

#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;
}

再次感谢大佬的代码

在这一串代码中主要变的就是各两个类中的对方函数的指针变为了弱指针(在需要的时候用.lock()给变回共享指针,使用完后置空)

这样就避免了双方都没有权限去析构对方类

特别感谢

(本章中用了不少这位大佬的代码,如有不妥,联系改正)

C++ 智能指针 - 全部用法详解_cpp智能指针特性_cpp_learners的博客-CSDN博客血的教训?不学智能指针,本人丢了一份工作。_cpp智能指针特性https://blog.csdn.net/cpp_learner/article/details/118912592

猜你喜欢

转载自blog.csdn.net/mumuemhaha/article/details/131743884