42 智能指针 auto_ptr, unique_ptr,shared_ptr,weak_ptr 整理

都是类模版

都不用开发者自己delete 指针。这是因为智能指针有自己管理指向对象的能力,包括释放指向的内存,因此开发者不要自己释放。

auto_ptr, (废弃)C++98

已经被弃用,替代方案是unique_ptr.

被弃用的原因:

1.不能使用vector等容器保存auto_ptr

2.不能从函数中返回auto_ptr类型

//已经被弃用,替代方案是unique_ptr.
//
//被弃用的原因:
//
//1.不能使用vector等容器保存auto_ptr
//
//2.不能从函数中返回auto_ptr类型

//3.在使用这样的code时,容易让开发者忘记,还有事情要做。


void main(){
	cout << "....." << endl;

	auto_ptr<string> pstr(new string("abc"));
	auto_ptr<string> pstr1(pstr);
	//代码执行到这里, pstr1中是"abc",pstr变成了empty,


	if (pstr.get() ==nullptr) {
		cout << "pstr.get() == nullptr" << endl; //结果走到这一行
	}
	else {
		cout << "pstr.get() exsit " << endl;
	}

	//如果后面我们再次使用pstr就会有问题。

	cout << "断点在这里";

}

unique_ptr, C11 (重点)

0、干啥的

独占式指针,意思是:同一个时间内,只有一个指针能够指向该指针。

当然,该对象的所有权还是可以移交出去的。

当这个unique_ptr被销毁的时候,指向的对象也会销毁。

在不指定删除器的情况下,大小和裸指针一样大。在指定了删除器的情况下,有可能比裸指针大。

   如果删除器是 函数指针,则会变大

        如果删除器是 lambda 表达式,则不会变大

1.如何实例化

void main() {
	//1初始化一般类型
	unique_ptr<int> ptr1;
	if (ptr1==nullptr) {
		cout << "ptr1==nullptr" << endl; //没有初始化的时候,是nullptr
	}
	else {
		cout << "ptr1!=nullptr" << endl;
	}
	unique_ptr<int> ptr2(new int(200));
	unique_ptr<int> ptr3(make_unique<int>(300));
	unique_ptr<int> ptr4 = make_unique<int>(400);
	auto ptr5 = make_unique<int>(500);

	//2初始化C++自带类型
	unique_ptr<string> ptr6;
	unique_ptr<string> ptr7(new string("abc"));
	unique_ptr<string> ptr8(make_unique<string>(3,'a'));
	unique_ptr<string> ptr9 = make_unique<string>("nihao");
	auto ptr10 = make_unique<string>("ggg");

	//3初始化自己写的类
	unique_ptr<Teacher138> ptr11;
	unique_ptr<Teacher138> ptr12(new Teacher138());
	unique_ptr<Teacher138> ptr13(make_unique<Teacher138>(100));
	unique_ptr<Teacher138> ptr14 = make_unique<Teacher138>();
	auto ptr15 = make_unique<Teacher138>(299);

	if (ptr11==nullptr) {
		cout << "ptr11 == nullptr "<<endl;

	}
	cout << ptr11.get() << endl;//这里没有报build error,说明unique_ptr类在处理get函数的时候,有做判断,即使是nullptr也会返回值

	//4初始化一般类型 数组
	unique_ptr<int[]> ptrarr16(new int[3]);
	for (int i = 0; i < 3;i++) {
		ptrarr16[i] = i * 9;
	}
	for (int i = 0; i < 3; i++) {
		cout<<"ptrarr16["<<i<<"]"<< ptrarr16[i]<<endl;
	}


	//5初始化string类型 数组
	unique_ptr<string[]> ptrarr17(new string[3]);
	for (int i = 0; i < 3; i++) {
		ptrarr17[i] = "abc";
	}
	for (int i = 0; i < 3; i++) {
		cout << "ptrarr17[" << i << "]" << ptrarr17[i] << endl;
	}


	//6.初始化自己写的类型 数组
	unique_ptr<Teacher138[]> ptrarr18(new Teacher138[3]);
	for (int i = 0; i < 3; i++) {
		ptrarr18[i].m_age = i * 10;
	}
	for (int i = 0; i < 3; i++) {
		cout << "ptrarr18[" << i << "].age = " << ptrarr18[i].m_age << endl;
	}

}

2.常规操作--包括常用函数

void main() {	
//由于是独占的
		unique_ptr<string> ptr20(new string("abcde"));
		//unique_ptr<string> ptr21 = ptr20;//build error
		//unique_ptr<string> ptr22(ptr20);//build error

		//unique_ptr<string> ptr23;
		//ptr23 = ptr20; //build error

	//那么怎么转移出去呢?要用move
		unique_ptr<string> ptr24 = move(ptr20);

		unique_ptr<int> ptr25(make_unique<int>(100));
		unique_ptr<int> ptr26 = move(ptr25);

		cout << "断点在这里1" << endl;

	//release()函数,放弃对指针的控制权,将unique_ptr中的裸指针返回
		//注意,一旦 release()后,得到的裸指针需要程序员自己收到的释放
		unique_ptr<int> ptr27(make_unique<int>(200));
		int * pint27 = ptr27.release();
		if (ptr27==nullptr) {
			cout << "after release , ptr27==nullptr" << endl;
		}
		delete pint27;

		cout << "断点在这里2" << endl;

		//reset函数,不带参数,表示释放智能指针指向的空间,并将ptr==nullptr
		unique_ptr<int> ptr28(make_unique<int>(300));
		ptr28.reset();
		if (ptr28 == nullptr) {
			cout << "after reset , ptr28==nullptr" << endl;
		}

		unique_ptr<int> ptr29(make_unique<int>(400));
		ptr29.reset(new int(500));
		if (ptr29 == nullptr) {
			cout << "after reset 带参数 , ptr29==nullptr" << endl;
		}
		else {
			cout << "after reset 带参数 , ptr29!=nullptr *ptr29  = " << *ptr29 << endl;
		}

		//get()返回裸指针,裸指针的声明周期还是由 智能指针掌握,不要自己delete
		int *ptrint29 = ptr29.get();
		//delete ptrint29;//runtime exception
}

2.1 一个疑问,我们知道unique_ptr<T>是独占的,那么如果函数返回一个unique_ptr<T> 行不行呢?

//一个疑问,我们知道unique_ptr<T>是独占的,
//那么如果函数返回一个局部变量的unique_ptr<T> 行不行呢?
//本质上是要看 unique_ptr类 有没有实现 移动拷贝函数,以及 移动拷贝函数中的写法
//我们知道,如果在返回局部变量的时候,会如果写了 移动拷贝函数 ,那么就会调用移动拷贝函数
//从debug 来看,unique_ptr类 应该是实现了 
unique_ptr<int> fun139() {
	return unique_ptr<int>(new int(98));
}


void main() {
	unique_ptr<int> ppr = fun139();
	cout << "断点在这里"<<endl;
	//如果fun139返回的不是局部变量,而是一个一直存在,就会有问题。因为unique_ptr是独占的
}

3.指定删除器

这里要和shard_ptr的删除器对比一下,比较好

void mydelete140(int *ptr) {
	cout << "mydelete140 " << endl;
	delete ptr;
	ptr = nullptr;
}
void main() {
	//先来看一下shared_ptr有删除器 时的用法,
	shared_ptr<int> shaptr(new int(100),mydelete140);
	//shared_ptr不用写删除器类型
	shaptr = nullptr;

	cout << "断点在这里" << endl;
	//unique_ptr 有删除器的用法,unique_ptr要写删除器类型
	using DELETEYTPE = void(*)(int *);
	unique_ptr<int, DELETEYTPE> uniptr(new int(200), mydelete140);

}

这里要注意的是:

unique_ptr<int, DELETEYTPE>,这一串是类型,

这意味着:如果我们要给vector中装 unique_ptr,如果删除器的类型不对,也装不进去,写代码的时候要额外的注意这点

shared_ptr,C11(重点)

0.内存图,大小为裸指针的2倍,多出来的第二个指针指向如下

1.共享所有权,可以被多个shared_ptr同时指向。

2.有额外的开销。

3.工作原理:使用了 引用计数,每个shared_ptr的拷贝都指向同样的内存。

所以,只有当最后一个指向该对象的shared_ptr指针不需要再指向该对象时候,那么这个shared_ptr才会去析构所指向的对象。

4.释放的时机有两种:

        a.当最后一个shared_ptr指向其他对象的时候

        b.当shared_ptr被析构的时候,比如说;在局部函数中,shared_ptr的作用域结束了,自然会被回收,紧跟着它指向的内存也会被释放

5. shared_ptr是类模型,且是explicit,不可以进行隐式类型的转换

6.初始化

shared_ptr<int> pi; // pi 是nullptr 

shared_ptr<int> pi1(); // 注意这里:这不是定义一个shared_ptr<int> 变量, 这里:pi1()是函数声明。因此不会有build error。

void func130(shared_ptr<int> tempptr) {//引用计数变成4 tempptr = shared_ptr 100 [4 strong refs] [make_shared]
	cout << "xxx" << endl;
}

shared_ptr<int> func131(shared_ptr<int>& temp) {
	return temp;
}


void main(){
	
	shared_ptr<int> pi; // pi 是nullptr 
	if (pi == nullptr) {
		cout << "pi == nullptr" << endl;
	}
	shared_ptr<int> pi1(); // 注意这里:这不是定义一个shared_ptr<int> 变量, 这里:pi1()是函数声明。因此不会有build error。
	//pi1 = make_shared<int>(100);//build error,pi1是函数声明。

	//由于shared_ptr是explicit的。因此要显示的实例化
	shared_ptr<int> pi2(new int);//pi2 = shared_ptr -842150451 [1 strong ref] [default] .........[ptr] = 0x000001f54ed67730 {-842150451}
	shared_ptr<int> pi3(new int());//pi3 = shared_ptr 0 [1 strong ref] [default].....[ptr] = 0x000001f54ed70bd0 {0}
	shared_ptr<int> pi4(new int(100));//pi4 = shared_ptr 100 [1 strong ref] [default]

	//让pi5 和 pi3指向同一块内存
	shared_ptr<int> pi5(pi3);// [ptr] = 0x000001f54ed70bd0 {0}

	//通过make_shared 实例化.
	//make_shared 是一个模版函数,
	//make_shared被认为是 安全,高效的。
	//make_shared生成的shared_ptr指针没有办法自定义删除器,
	//如下代码的意思是:通过 make_shared函数分配一块大小为int的空间,该空间的值是200,并用该空间的指针初始化 pi6
	shared_ptr<int> pi6(make_shared<int>(200));

	//打印结果
	cout << *pi6 << endl; //200

	//赋值
	pi = make_shared<int>(100);
	cout << *pi << endl;//100
	*pi6 = 999; //999
	cout << *pi6 << endl;


	//关于 shared_ptr的强引用指针的增加的例子。
	auto p6 = make_shared<int>(100); //强引用是1
	auto p7(p6);//强引用变成2
	auto p8(p7);//强引用变成3
	//当智能指针当做实参往函数里面传递的时候,强引用计数也会+1,但是当形参使用完毕后,又会减1
	func130(p7);
	//当func130方法执行完毕后,强引用又变回3

	
	//当函数返回值是智能指针时,计算器也会+1
	auto p9 = func131(p7); //强引用变成4


	//关于 shared_ptr的强引用指针的减少的例子。
	p9 = nullptr;//强引用变成3
	p8 = make_shared<int>(200);//强引用变成2, p8的指向变化的时候,也会-1

	cout << "断点在这里";
}

6.1 初始化数组 C++17

	//使用shared_ptr管理动态数组:
	shared_ptr<int[]> str7111(new int[3]());//new int[3]()带小括号,这三个int的值默认都会给0


	shared_ptr<int[]> str7(new int[3]);
	for (int i = 0; i < 3;i++) {
		str7[i] = i*3 + 6;
	}

	for (int i = 0; i < 3; i++) {
		cout << "str7[" << i<<"] = " <<str7[i]<< endl;
	}
	int aaaaaa = str7.use_count();//count 是 1

	shared_ptr<Teacher121[]> str8(new Teacher121[3]());//new Teacher121[3]()带小括号,不会给三个Teacher121赋任何值
	shared_ptr<Teacher121[]> str9(new Teacher121[3]);
	str8[0] = Teacher121(20);
	str8[1] = Teacher121(30);
	str8[2] = Teacher121(40);

	cout << "--------" << endl;
	for (int i = 0; i < 3;i++) {
		str8[i].print();
	}
	cout << "--------" << endl;

	str9[0] = Teacher121(200);
	str9[1] = Teacher121(300);
	str9[2] = Teacher121(400);


	cout << "--------" << endl;
	for (int i = 0; i < 3; i++) {
		str9[i].print();
	}
	cout << "--------" << endl;

6.2 初始化数组 C++17之前,这个要结合删除器,在 删除器的时候会一并介绍,

7.相关函数

long use_count

返回管理当前对象的不同 shared_ptr 实例(包含 this )数量。若无管理对象,则返回 ​0​ 。

多线程环境下, use_count 返回的值是近似的(典型实现使用 memory_order_relaxed 加载)

std::shared_ptr<T>::use_count



long use_count() const noexcept;
   
   

返回管理当前对象的不同 shared_ptr 实例(包含 this )数量。若无管理对象,则返回 ​0​ 。

多线程环境下, use_count 返回的值是近似的(典型实现使用 memory_order_relaxed 加载)

参数

(无)

返回值

管理当前对象的 shared_ptr 实例数量,或若无被管理对象则为 ​0​ 。

注意

常用使用包括
◦与 ​0​ 比较。若 use_count 返回零,则智能指针为空且不管理对象(无论被存储指针是否为空)。多线程环境下,这不隐含被管理对象的析构函数已完成。
◦与 1 比较。若 use_count 返回 1 ,则无其他拥有者。(被弃用成员函数 unique() 为此使用情况提供。)多线程环境中,这不隐含对象可以安全修改,因为先前拥有者对被管理对象的访问可能未完成,而因为新的共享拥有者可以同时引入,例如用 std::weak_ptr::lock 。
	//关于 shared_ptr的强引用指针的增加的例子。
	auto p6 = make_shared<int>(100); //强引用是1
	auto p7(p6);//强引用变成2
	auto p8(p7);//强引用变成3
	//当智能指针当做实参往函数里面传递的时候,强引用计数也会+1,但是当形参使用完毕后,又会减1
	func130(p7);
	//当func130方法执行完毕后,强引用又变回3

	
	//当函数返回值是智能指针时,计算器也会+1
	auto p9 = func131(p7); //强引用变成4


	//关于 shared_ptr的强引用指针的减少的例子。
	p9 = nullptr;//强引用变成3
	p8 = make_shared<int>(200);//强引用变成2, p8的指向变化的时候,也会-1


	int count = p6.use_count();//返回2

bool unique()

std::shared_ptr<T>::unique


bool unique() const noexcept;
  (C++17 中弃用)
(C++20 中移除) 
   

检查 *this 是否管理当前对象的仅有 shared_ptr 实例,即是否 use_count() == 1 。

参数

(无)

返回值

若 *this 否管理当前对象的仅有 shared_ptr 实例则为 true ,否则为 false 。

注意

此函数于 C++17 中被弃用并于 C++20 中移除,因为 use_count 在多线程环境中只是估计(见 use_count 中的“注意”)。

std::shared_ptr<T>::reset

如果reset()括号中没有参数,则 ptr==nullptr.这意味着,如果ptr是唯一的指向内存的指针,则内存会被释放。如果有多个指针指向该内存,则 强引用-1.

如果reset(Y* newptr)括号中有参数,会将ptr的指向 newptr 的内存。如果ptr是唯一的之前的指向内存的指针,则内存会被释放。如果有多个指针指向该内存,则 强引用-1.

std::shared_ptr<T>::reset




void reset() noexcept;
 (1) (C++11 起) 

template< class Y >
void reset( Y* ptr );
 (2) (C++11 起) 

template< class Y, class Deleter >
void reset( Y* ptr, Deleter d );
 (3) (C++11 起) 

template< class Y, class Deleter, class Alloc >
void reset( Y* ptr, Deleter d, Alloc alloc );
 (4) (C++11 起) 
   

以 ptr 所指向的对象替换被管理对象。能可选地提供删除器 d ,之后在无 shared_ptr 对象占有该对象时以之销毁新对象。默认以 delete 表达式为删除器。始终选择对应提供类型的 delete 表达式,这是函数以使用分离的形参 Y 的模板实现的理由。

若 *this 已占有对象,且它是最后一个占有该对象的 shared_ptr ,则通过所占有的删除器销毁对象。

若 ptr 所指向的对象已被占有,则函数通常会导致未定义行为。

1) 释放被管理对象的所有权,若存在。调用后, *this 不管理对象。等价于 shared_ptr().swap(*this); 。

2-4) 以 ptr 所指向对象替换被管理对象。 Y 必须是完整类型且可隐式转换为 T 。另外:

2) 以 delete 表达式为删除器。合法的 delete 表达式必须可用,即 delete ptr 必须为良式,拥有良好定义行为且不抛任何异常。等价于 shared_ptr<T>(ptr).swap(*this); 。

3) 以指定的删除器 d 为删除器。 Deleter 必须对 T 类型可调用,即 d(ptr)必须为良构,拥有良好定义行为且不抛任何异常。 Deleter 必须可复制构造 (CopyConstructible) ,且其复制构造函数和析构函数必须不抛异常。等价于 shared_ptr<T>(ptr, d).swap(*this); 。

4) 同 (3) ,但额外地用 alloc 的副本分配内部使用的数据。 Alloc 必须是分配器 (Allocator) 。复制构造函数和析构函数必须不抛异常。等价于 shared_ptr<T>(ptr, d, alloc).swap(*this); 。

参数

ptr - 指向要取得所有权的对象的指针 
d - 为删除对象而存储的删除器 
alloc - 内部存储所用的分配器 

返回值

(无)

异常

2) 若无法获得要求的额外内存则为 std::bad_alloc 。可能因其他错误抛出实现定义的异常。若出现异常则调用 delete ptr 。

3-4) 若无法获得要求的额外内存则为 std::bad_alloc 。可能因其他错误抛出实现定义的异常。若出现异常则调用 d(ptr) 。

	//reset函数,没有参数
	shared_ptr<string> str1(new string("abc"));
	shared_ptr<string> str2 = str1;// str1 和 str2都指向“abc”这块内存,强引用数量2
	str1.reset();//这时候 str1变成nullptr,指向“abc”这块内存的强引用数量变成1
	str2.reset();// 这时候 str2变成nullptr,指向“abc”这块内存的强引用数量变成0,于是这块内存被释放


	shared_ptr<string> str3(new string("def"));
	shared_ptr<string> str4(str3);// str3 和 str4都指向“def”这块内存,强引用数量2
	str4.reset(new string("iou")); //str4指向新的内存,str3指向的内存的 强引用变成1,str4的强引用变成1,不过str4指向另一个内存空间了
	//str3 [ptr] = 0x00000237028fbc30 "def"
	//strs4 [ptr] = 0x00000237028fba00 "iou"

std::shared_ptr<T>::get


返回存储的指针。

注意,返回的存储的指针,我们也叫做裸指针,

由于这个裸指针还是依赖于 shared_ptr存在,因此程序员不要手动的delete 它,如果delete了,那么shared_ptr在释放这块内存的时候,delete的时候,这块指向的空间会被delete两次,就会造成程序异常。

std::shared_ptr<T>::get



T* get() const noexcept;
  (C++17 前) 

element_type* get() const noexcept;
  (C++17 起) 

   

返回存储的指针。

参数

(无)

返回值

存储的指针。

注意

shared_ptr 可能在存储指向一个对象的指针时共享另一对象的所有权。 get() 返回存储的指针,而非被管理指针。

	shared_ptr<string> str6(new string("qqq"));
	string * s = str6.get();
	cout << "s = " << s << "    *s = " << *s << endl;
	//delete s;//程序员不要手动的delete,否则会运行时异常。
	*s = "www";
	cout << "s = " << s << "    *s = " << *s << endl;

	//那么get()方法的应用场景主要是为了第三方库,很大程度上第三方库的参数可能是 裸指针,而不是智能指针

std::shared_ptr<T>::swap  这玩意不常用,这里就不举例了

std::shared_ptr<T>::swap



void swap( shared_ptr& r ) noexcept;
  (C++11 起) 
   

交换 *this 与 r 的存储指针值与所有权。不调整引用计数,若它们存在。

参数

r - 要与之交换内容的智能指针 

返回值

(无)

8. 指定删除器

从前面的知识中知道,在使用智能指针的时候,我们不需要手动的delete,那么C++编译器是如何自动的delete的呢?这里就引出了 删除器 的概念。

1.为什么要自己指定删除器呢?在C++17之前shared_ptr如果管理的是动态数组,则需要自己指定删除器。

实际上C++17之前 shared_ptr内部就是 使用关键字 delete ptr完成的。也正是由于由于shared_ptr 内部只有delete ptr,没有delete[] ptr,因此在C++17之前 如果shared_ptr管理的是动态数组,那么就需要自己指定删除器。

如果知道是C++17之前不支持呢?

参考C++ 文档:shared_ptr中的关于[]的介绍

operator[]

(C++17)

提供到被存储数组的带下标访问
(公开成员函数)

2.什么是 删除器

a.删除器可以是一个函数,返回值是void,参数是( T * p);

class Teacher122 {

public:
	Teacher122() {
		cout << "Teacher122 没有参数的构造函数被执行   " << this << endl;
	}

	Teacher122(int age) :m_age(age) {
		cout << "Teacher122 int age 构造函数被执行   " << this<< endl;
	}

	void print() {
		cout << "age = " << this->m_age << "   " << this << endl;
	}

	~Teacher122() {
		cout << "Teacher122 析构函数被调用   " << this << endl;
	}

	void setAge(int age) {
		this->m_age = age;
	}
private:
	int m_age;
};


void mydelete(int *p) {
	cout << "mydelete int called" << endl;
	delete p;
	p = nullptr;
}

void mydeletearr(int *p) {
	cout << "mydeletearr int called" << endl;
	delete [] p;
	p = nullptr;
}

void mydeleteTeacher122(Teacher122 *p) {
	cout << "mydeleteTeacher122  called" << endl;
	delete[] p;
	p = nullptr;
}

void main() {
	//C++17之前 shared_ptr管理 普通 怎么写
	shared_ptr<int> intptr(new int(200),mydelete);
	intptr = nullptr;
	cout << "-----------" << endl;
	//C++17之前 shared_ptr管理 数组怎么写?由于没有下标[]可以操作,因此,只能通过get得到裸指针,然后通过裸指针操作数据
	shared_ptr<int> intarrptr(new int[3], mydeletearr);
	for (int i = 0; i < 3;i++) {
		//注意,要获得数据,只能通过get()方法得到裸指针,该裸指针肯定是指向int的,因此每次加1都是跳4个字节,因此可以通过 *(ptr + i)给数据赋值。
		*(intarrptr.get()+i) = 9 * i;
	}
	
	for (size_t i = 0; i < 3; i++)
	{
		cout << "*(intarrptr.get()+i = " << *(intarrptr.get() + i) << endl;
	}
	intarrptr = nullptr;

	//结果:
	//*(intarrptr.get()+i = 0
	//*(intarrptr.get() + i = 9
	//* (intarrptr.get() + i = 18
	//mydeletearr int called


	C++17之前 shared_ptr管理 普通类 怎么写
	shared_ptr<Teacher122> pteacher1(new Teacher122());
	pteacher1->print();
	pteacher1->setAge(78);
	pteacher1->print();
	cout << "mmm" << endl;
	pteacher1 = nullptr;
	//Teacher122 没有参数的构造函数被执行   00000130B9427730
	//	age = -842150451   00000130B9427730
	//	age = 78   00000130B9427730
	//	mmm
	//	Teacher122 析构函数被调用   00000130B9427730



	C++17之前 shared_ptr管理 普通类数组 怎么写
	shared_ptr<Teacher122> pteacherarr(new Teacher122[3], mydeleteTeacher122);
	for (int i = 0; i < 3;i++) {
		//get()函数返回裸指针
		Teacher122* temp = pteacherarr.get() + i;
		temp->setAge((i + 1) * 30);
	}

	for (int i = 0; i < 3; i++) {
		Teacher122* temp = pteacherarr.get() + i;
		temp->print();
	}

	//Teacher122 没有参数的构造函数被执行   00000130B94322F8
	//	Teacher122 没有参数的构造函数被执行   00000130B94322FC
	//	Teacher122 没有参数的构造函数被执行   00000130B9432300
	//	age = 30   00000130B94322F8
	//	age = 60   00000130B94322FC
	//	age = 90   00000130B9432300
	//	mydeleteTeacher122  called
	//	Teacher122 析构函数被调用   00000130B9432300
	//	Teacher122 析构函数被调用   00000130B94322FC
	//	Teacher122 析构函数被调用   00000130B94322F8

}

b.删除器可以是一个lambda表达式

	//b.删除器可以是一个lambda表达式
	cout << "删除器是一个lambda表达式start " << endl;
	shared_ptr<Teacher122> pteacherarr2(new Teacher122[3], [](Teacher122 *p) {
		delete[] p;
		p = nullptr;
	});
	for (int i = 0; i < 3; i++) {
		//get()函数返回裸指针
		Teacher122* temp = pteacherarr2.get() + i;
		temp->setAge((i + 1) * 50);
	}

	for (int i = 0; i < 3; i++) {
		Teacher122* temp = pteacherarr2.get() + i;
		temp->print();
	}
	cout << "删除器是一个lambda表达式end " << endl;

	//删除器是一个lambda表达式start
	//	Teacher122 没有参数的构造函数被执行   0000025294FC1D58
	//	Teacher122 没有参数的构造函数被执行   0000025294FC1D5C
	//	Teacher122 没有参数的构造函数被执行   0000025294FC1D60
	//	age = 50   0000025294FC1D58
	//	age = 100   0000025294FC1D5C
	//	age = 150   0000025294FC1D60
	//	删除器是一个lambda表达式end
	//	Teacher122 析构函数被调用   0000025294FC1D60
	//	Teacher122 析构函数被调用   0000025294FC1D5C
	//	Teacher122 析构函数被调用   0000025294FC1D58

c.可用default_delete来做删除器。

default_delete是标准库里的模板类

	cout << "删除器是一个default_delete 标准库模版 start " << endl;
	shared_ptr<Teacher122> pteacherarr3(new Teacher122[3], default_delete<Teacher122[]>());
	for (int i = 0; i < 3; i++) {
		//get()函数返回裸指针
		Teacher122* temp = pteacherarr3.get() + i;
		temp->setAge((i + 1) * 60);
	}

	for (int i = 0; i < 3; i++) {
		Teacher122* temp = pteacherarr3.get() + i;
		temp->print();
	}
	cout << "删除器是一个default_delete 标准库模版 end " << endl;

	//删除器是一个default_delete 标准库模版 start
	//	Teacher122 没有参数的构造函数被执行   000001CD1E9D2488
	//	Teacher122 没有参数的构造函数被执行   000001CD1E9D248C
	//	Teacher122 没有参数的构造函数被执行   000001CD1E9D2490
	//	age = 60   000001CD1E9D2488
	//	age = 120   000001CD1E9D248C
	//	age = 180   000001CD1E9D2490
	//	删除器是一个default_delete 标准库模版 end
	//	Teacher122 析构函数被调用   000001CD1E9D2490
	//	Teacher122 析构函数被调用   000001CD1E9D248C
	//	Teacher122 析构函数被调用   000001CD1E9D2488
d.make_shared无法指定删除器,只能使用默认的,因此动态生成数组不能使用make_shared

3.注意的是C++17之后shared_ptr已经可以直接管理动态数组了

写法如下,

	//使用shared_ptr管理动态数组:
	shared_ptr<int[]> str7111(new int[3]());//new int[3]()带小括号,这三个int的值默认都会给0


	shared_ptr<int[]> str7(new int[3]);
	for (int i = 0; i < 3;i++) {
		str7[i] = i*3 + 6;
	}

	for (int i = 0; i < 3; i++) {
		cout << "str7[" << i<<"] = " <<str7[i]<< endl;
	}
	int aaaaaa = str7.use_count();//count 是 1

	shared_ptr<Teacher121[]> str8(new Teacher121[3]());//new Teacher121[3]()带小括号,不会给三个Teacher121赋任何值
	shared_ptr<Teacher121[]> str9(new Teacher121[3]);
	str8[0] = Teacher121(20);
	str8[1] = Teacher121(30);
	str8[2] = Teacher121(40);

	cout << "--------" << endl;
	for (int i = 0; i < 3;i++) {
		str8[i].print();
	}
	cout << "--------" << endl;

	str9[0] = Teacher121(200);
	str9[1] = Teacher121(300);
	str9[2] = Teacher121(400);


	cout << "--------" << endl;
	for (int i = 0; i < 3; i++) {
		str9[i].print();
	}
	cout << "--------" << endl;

9.shared_ptr的使用陷阱

a.当返回值是 shared_ptr的时候,要注意是不是有变量接受。

//9.shared_ptr的使用陷阱
//a.当返回值是 shared_ptr 的时候,要注意是不是有变量接受。
//如果没有变量接受,那么离开 func135方法的作用域后,shared_ptr 就会销毁
//如果有变量接受,则shared_ptr的强引用+1
shared_ptr<int> func135() {
	return make_shared<int>(199);
}
void main() {
	func135();

	cout << "duandian 1" << endl;
	shared_ptr<int> aa = func135();//aa = shared_ptr 199 [1 strong ref] [make_shared]

	cout << "duandian 2" << endl;
}

b.慎用裸指针

最好不要 让 裸指针和智能指针混用。

b1如果混用,注意:一个裸指针只能初始化一个shared_ptr,

一旦让一个裸指针初始化了多个shared_ptr.

当ptr1销毁的时候,裸指针也会销毁。但是ptr2还是指向的裸指针,就会有问题。

b2,一旦裸指针初始化了智能指针,就不要去delete 裸指针了。

b3, 裸指针作为临时对象shared_ptr的初始化参数,在临时对象shared_ptr销毁的时候,裸指针也会被释放。

b4.  shared_ptr通过get方法获得的指针不要绑定在其他的指针上,也不要delete

void fun136(shared_ptr<int> tempptr){
	cout << "fun136 called" << endl;
}

void main() {
	//b.慎用裸指针
	//	最好不要 让 裸指针和智能指针混用。

	//	b1如果混用, 注意:一个裸指针只能初始化一个shared_ptr,
	//	一旦让一个裸指针初始化了多个shared_ptr.
	//	当ptr1销毁的时候,裸指针也会销毁。但是ptr2还是指向的裸指针,就会有问题。

	int *p = new int(100);
	shared_ptr<int> ptr1(p);
	//shared_ptr<int> ptr2(p);//让裸指针p初始化了2个shared_ptr,一旦运行,就run time exception



	//	b2,一旦裸指针初始化了智能指针,这个裸指针的生命管理就交给智能指针了,就不要去delete 裸指针了。
	int *p2 = new int(200);
	shared_ptr<int> ptr3(p2);
	//delete p2;//手动delete,就会有run time exception


	//	b3, 裸指针作为临时对象shared_ptr的初始化参数,在临时对象shared_ptr销毁的时候,裸指针也会被释放。
	int *p3 = new int(300);
	fun136(shared_ptr<int>(p3));
	cout << "duandian" << endl;//由于 临时变量 shared_ptr<int>(p3) 传递到 fun136方法的时候,强引用计数不会+1,因此当func136执行完毕后,引用计数-1变成0,临时便令shared_ptr<int>(p3)会被销毁,进而裸指针p也会被delete
	//注意这里p3已经被释放了,就不要再用了


	//	b4.shared_ptr通过get方法获得的指针不要绑定在其他的指针上,也不要delete

	shared_ptr<int> p5(new int(500));
	shared_ptr<int> p6(make_shared<int>(600));
	int * pp5 = p5.get();

	cout << "duandian 2 " << endl;
	//shared_ptr<int> p7(pp5);//这里不能将裸指针 pp5 绑定到其他 智能指针上了
	//delete pp5;//也不要delete 裸指针
}

b5.不要把类对象this 做为shared_ptr的参数

//不要把类对象this 做为shared_ptr的参数,这其实很好理解。
//假设被人调用了,那么this这个裸指针 就被两个不同的 shared_ptr指向了。
//class Teacher136 {
//public:
//	shared_ptr<Teacher136> getTeacher136() {
//		return shared_ptr<Teacher136>(this);
//	}
//};

//上述问题如何解决呢?
//让 Teacher136 继承enable_shared_from_this<Teacher137>.
//返回shared_from_this();

class Teacher137 :public enable_shared_from_this<Teacher137>{
public:
	shared_ptr<Teacher137> getTeacher137() {
		return shared_from_this();
	}
	int mage;
};

void main() {
	//Teacher137 tea;
	//tea.mage = 10;
	//注意使用,因为返回的shared_from_this(),那么意味着之前一定是要有 智能指针指向这个this的,因此要先弄出来一个智能指针
	Teacher137 *pt = new Teacher137();
	shared_ptr<Teacher137> ptea(pt);
	shared_ptr<Teacher137> ptea1 = ptea->getTeacher137();
	ptea->mage = 90;
	cout << pt->mage << "   " << ptea1->mage << endl;
}

10.移动语义也使用于智能指针

shared_ptr<int> p1(make_shared<int>(100));

shared_ptr<int> p2 = move(p1);

	//10.移动语义也使用于智能指针

	shared_ptr<int> p1(make_shared<int>(100));
	shared_ptr<int> p2 = move(p1);

	//  从测试结果看: p1变成empty 了,p2变成指向 100的那块内存了,
	//	这说明shared_ptr类中对于移动构造的实现是:转移裸指针的所属,并将强引用那块的指针也转移了
	cout << "   " << endl;

11.使用建议: 优先使用 make_shared<T>(200),make_shared被认为是安全的,效率高的。

weak_ptr C11(辅助shared_ptr)

是个啥?

类模版

用来辅助shared_ptr进行工作的。

shared_ptr  和  weak_ptr 所指向对象的引用计数,有两种,一种是强引用,一种是弱引用。

强引用指的是 shared_ptr指向的个数

弱引用指的是 weak_ptr指向的个数。

weak_ptr指向一个由shared_ptr管理的对象。

当shard_ptr指向的对象的强引用 计数变成0的时候,该对象会析构,这时候是不用看 weak_ptr的弱引用计数的。

weak_ptr 不能独立存在,必须要依附于shard_ptr存在。

有啥用?

weak_ptr 监视 shard_ptr 的生命周期用的,是对shard_ptr的一种扩展。

通过 use_count(); 可以获取强引用的计数

通过 expired(); 是否过期,如use_count()返回0,则返回true。

如上两个函数给人的感觉好像重复了, 只是多了一个判断?查了下 C++的文档,好像也没有特殊的地方。有知道的小伙伴可以帮忙回复一下。

通过 reset();可以将自己的这个弱引用==nullptr,弱引用计数-1

lock();检查weak_ptr 指向的对象是否存在,如存在,返回一个指向该对象的 shared_ptr(这时候对象的强引用计数就会+1)

怎么用?

void main() {

	shared_ptr<string> str1(new string("abc"));
	weak_ptr<string> wstr2(str1);

	cout << wstr2.use_count() << endl;//1,use_count()可以获取强引用的计数

	shared_ptr<string> str2(str1);
	shared_ptr<string> str3(str1);
	cout << wstr2.use_count() << endl;//3,use_count()可以获取强引用的计数

	cout << "---------------" << endl;

	auto str5 = make_shared<string>("def");
	weak_ptr<string> wstr6(str5);
	weak_ptr<string> wstr7(str5);
	weak_ptr<string> wstr8;
	wstr8 = wstr7;//wstr8 = weak_ptr "def" [1 strong ref, 3 weak refs] [make_shared]

	

	wstr8.reset();
	// 当wstr8 reset之后,str8为empty
	//str7的值是: wstr7 = weak_ptr "def"[1 strong ref, 2 weak refs][make_shared]

	cout << "......" << endl;

	//lock的使用
	if (!wstr7.expired()) {
		shared_ptr<string> wstr10 = wstr7.lock();
		if (wstr10 != nullptr) {
			cout << wstr7.use_count() << endl; // wstr7 = weak_ptr "def" [2 strong ref, 2 weak refs] [make_shared]
		}
	}
	
	cout << wstr7.use_count() << endl; // 出了作用域后,wstr10的强引用会被-1. 因此wstr7 = weak_ptr "def" [1 strong ref, 2 weak refs] [make_shared]
}

使用场景?

监视 shard_ptr 的生命周期。

有哪些坑点?

  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值