C++指针(含智能指针)

指针简单的理解就是一个存放地址的变量,在C++中可以通过指针来操控内存;

指针的作用:
1.函数的值传递,无法通过调用函数,来修改函数的实参 ;
2.被调用函数需要提供更多的“返回值”给调用函数 ;
3.减少值传递时带来的额外开销,提高代码执行效率;

注意:
在32位系统中, int类型占4个字节,指针占4个字节;
在64位系统中, int类型占4个字节,指针占8个字节;

指针的定义:
普通指针

#include 

using namespace std;

int main(void) {

	//指针定义: 数据类型 * 指针名称
	//一般定义指针时先预设为空
	int* p = NULL;
	int num = 9;

	//指针p指向num, p存放的就是num的地址
	p = #
	
	//打印num和p查看地址
	cout << "num addr:" << &num << endl;
	cout << "p:" << p << endl;

	//通过指针p可以修改num的值,*p通过*号对指针p解引,获取指针保存的数据
	*p = 20;
	cout << "num:" << num << endl;
	cout << "p:" << *p << endl;

	system("pause");
	return 0;
}

运行结果:
C++指针(含智能指针)_第1张图片
空指针:
就是值为 0 的指针。(任何程序数据都不会存储在地址为 0 的内存块中,它是被操作系 统预留的内存块。)
如: int *p=NULL;
使用空指针的目的就是为了避免访问非法数据;
指针不再使用时,可以设为空指针;

坏指针:
就是没有初始化的指针,和直接赋值的指针;
如: int *p;(没有初始化) int *p=10;(直接在定义时就赋值);

const修饰指针
就是通过const修饰指针所指向的数据进行限制;

#include 

using namespace std;

int main(void) {

	int n = 30;
	int k = 60;
	char q = 'c';

	//可以指向同一种数据类型的变量,但是不允许修改指向变量的值,有两种写法
	const int* p = &n;
	p = &k;
	int const* ptr = &k;
	ptr = &n;
	//p=&q,  ptr=&q,  *p=100,*ptr=200 都是错误的

	cout << "只允许指向同样的数据类型:" << endl;
	cout << "*p=" << *p << "\n" << "*ptr=" << *ptr << endl;

	//不允许指向别的地址,但是可以修改指向变量的值
	cout << "\nn=" << n << endl;
	int* const ktr = &n;
	*ktr = 99;
	//ktr = &k;错误的
	cout << "不允许指向别的地址,但是可以修改原来的值:" << endl;
	cout << "n=" << n << "\n" << "*ktr=" << *ktr << endl;

	//不允许指向别的地址,同时也不允许修改指向变量的值
	const int* const qtr = &n;
	//qtr = 20, qtr = &k;这样都是错误的
	cout << "不能做任何修改:" << endl;
	cout << "n=" << n << "\n" <<"*qtr="<<*qtr << endl;

	system("pause");
	return 0;
}

C++指针(含智能指针)_第2张图片
注意:
const 离数据类型(int)近,还是离指针变量名近, 离谁进就修饰谁,谁就不能变;

指针的运算
自增:
(1)指针与整数的运算,指针加减数字表示的意义是指针在数组中位置的移动; 对于整数部分而言,它代表的是一个元素,对于不同的数据类型,其数组的元素占 用的字节是不一样的, 比如指针 + 1,并不是在指针地址的基础之上加 1 个地址,而是在这个指针地址的 基础上加 1 个元素占用的字节数:

  1. 如果指针的类型是 char*,那么这个时候 1 代表 1 个字节地址;
  2. 如果指针的类型是 int*,那么这个时候 1 代表 4 个字节地址;
  3. 如果指针的类型是 float*,那么这个时候 1 代表 4 个字节地址;
  4. 如果指针的类型是 double*,那么这个时候 1 代表 8 个字节地址。

(2)通用公式: 数据类型 *p; p + n 实际指向的地址:p 基地址 + n * sizeof(数据类型) p - n 实际指向的地址:p 基地址 - n * sizeof(数据类型) 比如:

1.对于 int 类型,比如 p 指向 0x0061FF14,则: p+1 实际指向的是 0x0061FF18,与 p 指向的内存地址相差 4 个字节; p+2 实际指向的是 0x0061FF1C,与 p 指向的内存地址相差 8 个字节

2.对于 char 类型,比如 p 指向 0x0061FF28,则: p+1 实际指向的是 0x0061FF29,与 p 指向的内存地址相差 1 个字节; p+1 实际指向的是 0x0061FF2A,与 p 指向的内存地址相差 2 个字节

自减:
(1)指针和指针可以做减法操作,但不适合做加法运算;
(2)指针和指针做减法适用的场合:两个指针都指向同一个数组,相减结果为两个指针之 间的元素数目,而不是两个指针之间相差的字节数。

比如:int int_array[4] = {12, 34, 56, 78}; int *p_int1 = &int_array[0]; int *p_int2 = &int_array[3]; p_int2 - p_int1 的结果为 3,即是两个之间之间的元素数目为 3 个。 如果两个指针不是指向同一个数组,它们相减就没有意义。

(3)不同类型的指针不允许相减,比如 char *p1; int *p2; p2-p1 是没有意义的

二级指针
二级指针也是一个普通的指针变量,只是它里面保存的值是另外一个一级指针的地址;

二级指针的用途:

  1. 普通指针可以将变量通过参数“带入”函数内部,但没办法将内部变量“带出”函数;
  2. 二级指针可以不但可以将变量通过参数函数内部,也可以将函数内部变量 “带出”到函 数外部;
#include 

using namespace std;

int main(void) {

	int n = 30;
	int* p = &n;
	int** ptr = &p;//定义一个二级指针

	//通过普通指针修改指向变量的值
	*p = 60;
	cout << "n=" << n << "\n" << "*p=" << *p << endl;

	//通过二级指针修改普通指针的值,同时也把普通指针指向的变量的值修改
	**ptr = 90;
	cout << "使用二级指针:" << endl;
	cout <<"n="<< n <<"\n"<<"*p="<<*p<< endl;

	//依此类推,可以使用三级指针改变二级指针,四级改变三级,但是这样做没有太多作用

	system("pause");
	return 0;
}

C++指针(含智能指针)_第3张图片

指针数组
存储指针的数组, 定义: 数据类型 *指针数组名[元素个数];

#include 

using namespace std;

int main(void) {

	//定义一个二维数组
	int array[2][2] = { {164,168},{98,88} };

	//定义一个有两个元素的指针数组,每个元素都是一个指针变量
	int* ptr[4] = {NULL};//把指针数组中的指针变量初始化为空

	//使用指针数组访问数组的值,循环的方式访问定义的指针数组的元素必须要
	//和二维数组相乘的值一样,不使用循环访问就可以随意定义元素的数量;
	ptr[0] = &array[1][1];
	cout << "ptr[0]=" << *ptr[0] << endl;
	for (int i = 0; i < 4; i++) {//4为二维数组的两个数相乘的值2*2=4;
		ptr[i] = &array[0][i];
		cout <<"ptr["<<i<<"]="<< *ptr[i] << endl;
	}

	system("pause");
	return 0;
}

C++指针(含智能指针)_第4张图片

数组指针:
指向数组的指针; 数据类型(int) (*指针名称)[指向成员的数量];
如: int (*p)[2]; 定义一个指向两个成员的数组的指针,
访问元素的方式有两种:
数组法: (*p)[i];
指针法: *((*p)+i);

#include 

using namespace std;

int main(void) {

	//定义一个二维数组
	int array[2][2] = { {164,168},{98,88} };

	//定义一个可以指向两个成员的数组指针,把值初始化为空
	int(*p)[2] = {NULL};

	//访问二维数组中的第一排元素的值,然后使用循环打印值
	p = &array[0];
	for (int i = 0; i < 2; i++) {
		cout <<"(*p)["<<i<<"]="<< (*p)[i] << endl;
	}

	system("pause");
	return 0;
}

C++指针(含智能指针)_第5张图片

数组与指针的区别:
数组:数组是用于储存多个相同类型数据的集合。
指针:指针是一个变量,但是它和普通变量不一样,它存放的是其它变量在内存中的地址。

  1. 赋值数组:只能一个一个元素的赋值或拷贝 指针:指针变量可以相互赋值 ;

  2. 表示范围 数组有效范围就是其空间的范围,数组名使用下表引用元素,不能指向别的数组 指针可以指向任何地址,但是不能随意访问,必须依附在变量有效范围之内;

  3. sizeof 数组:数组所占存储空间的内存:sizeof(数组名) 数组的大小:sizeof(数组名)/sizeof(数据类型) 指针:在 32 位平台下,无论指针的类型是什么,sizeof(指针名)都是;

  4. 在 64 位平台下,无论指针的类型是什么,sizeof(指针名)都是 8.

指针数组:
int *qishou[2];//定义一个有两个元素的指针数组,每个元素都是一个指针变量;

数组指针:
int (*p)[3]; //定义一个指向三个成员的数组的指针;

传参:
数组传参时,会退化为指针!
(1)退化的意义:C 语言只会以值拷贝的方式传递参数,参数传递时,如果只拷贝整个数 组,效率会大大降低,并且在参数位于栈上,太大的数组拷贝将会导致栈溢出。

(2)因此,C 语言将数组的传参进行了退化。将整个数组拷贝一份传入函数时,将数组名 看做常量指针,传数组首元素的地址。

void 类型指针
void => 空类型
void* => 空类型指针,只存储地址的值,丢失类型,无法访问,要访问其值,我们必须对这个指 针做出正确的类型转换,然后再间接引用指针。

所有其它类型的指针都可以隐式自动转换成 void 类型指针,反之需要强制转换

#include 

using namespace std;

int main(void) {

	int arr[] = { 1,6,9 };
	char k = 'A';

	//定义一个void类型的指针
	void* ptr = arr;
	//ptr++; 不允许,void指针不允许进行算术运算
	//类型转换后就可以进行算术运算了
	int* p = (int*)ptr;
	for (int i = 0; i < sizeof(p)-1; i++) {
		cout << p[i] << ",";
	}
	cout << endl;
	//其他类型可以自动转换成void* 指针
	void* kp = &k;
	char* ktr = (char*)kp;
	cout << *ktr << endl;

	system("pause");
	return 0;
}

在这里插入图片描述

函数指针:
函数指针的定义:
把函数声明移过来,把函数名改成 (* 函数指针名)
如: int (*fp)(const void *, const void *);

#include 

using namespace std;

int test(const void* a, const void* b) {
	int* a1 = (int*)a;
	int* b1 = (int*)b;

	return *a1 - *b1;
}
int main(void) {

	//函数也是有地址的
	cout <<"函数int test addr :"<< &test << endl;

	//定义函数指针
	int (*ptr)(const void*, const void*);

	/*贝尔实验室的C和UNIX的开发者采用第1种形式,
	而伯克利的UNIX推广者却采用第2 种形式ANSI C 兼容了两种方式*/

	ptr = &test;
	int x = 0, y = 3;
	//第1种,按普通指针解引的放式进行调用,(*ptr) 等同于test函数
	(*ptr)(&x, &y);

	//第2种 直接调用
	ptr(&x, &y);

	//qsort对数组排序(qsort可以对任何类型的数组进行排序)
	int arr[] = { 6,1,9,2,7 };

	//两种访问方式都可以
	qsort(arr, sizeof(arr) / sizeof(int), sizeof(int), &test);//函数
	qsort(arr, sizeof(arr) / sizeof(int), sizeof(int), ptr);//函数指针
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++) {
		cout << arr[i] << ",";
	}
	cout << endl;

	system("pause");
	return 0;
}

C++指针(含智能指针)_第6张图片

隐式指针: 引用
a) 在C++中新增加了引用的概念 ;属于C++编译器对C的扩展;
b) 引用可以看作一个已定义变量的别名
c) 引用的语法:Type& name = var;
d) 引用做函数参数那?(引用作为函数参数声明时不进行初始化)

引用做函数参数
普通引用在声明时必须用其它的变量进行初始化,
引用作为函数参数声明时不进行初始化

引用的意义
1)引用作为其它变量的别名而存在,因此在一些场合可以代替指针
2)引用相对于指针来说具有更好的可读性和实用性

引用的本质
1)引用在C++中的内部实现是一个常指针 Type& name <- ->Type* const name

2)C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大 小与指针相同。

3)从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间。这是C++ 为了实用性而做出的细节隐藏

引用结论
1)当实参传给形参引用的时候,只不过是c++编译器帮我们程序员手工取了一个实参地 址,传给了形参引用(常量指针)

2)当我们使用引用语法的时,我们不去关心编译器引用是怎么做的 当我们分析奇怪的语法现象的时,我们才去考虑c++编译器是怎么做的

常引用
在 C++中可以声明 const 引用 语法: const Type& name = var; const 引用让变量拥有只读属性;

const 引用结论:
1)const & int e 相当于 const int * const e
2)普通引用 相当于 int *const e1
3)当使用常量(字面量)对const引用进行初始化时,C++编译器会为常量值分配空间, 并将引用名作为这段空间的别名
4)使用字面量对const引用初始化后,将生成一个只读变量

#include 

using namespace std;

int main(void) {

	int num = 90;

	//引用的形式与指针类似,但是引用在定义时就要赋值,因为它只是一个别名
	int& k = num;

	//通过引用修改常量的值
	k = 33;//与num=33; 这样重新赋值的意义差不多
	cout << num << endl;

	num = 66;
	cout << num << endl;

	system("pause");
}

C++指针(含智能指针)_第7张图片

智能指针

实现自动释放内存,可以有效地避免内存泄漏,所以在代码中需要分配内存时,使用智能指针可以让开发者少在内存泄漏这方面出错,从而提高程序的效率;

C++98提供了auto_ptr模板的智能指针;
C++11增加unique_ptr, shared_ptr和weak_ptr三个模板的智能指针;

auto_ptr使用详解(C++98提供)

auto_ptr是C++98定义的智能指针模板,其定义了管理指针的对象,可以将new获得(直接或者间接)的地址赋值给这种对象,当对象过期时,其析构函数将使用delete来释放内存;

用法:
头文件: #include
用法: auto_ptr<数据类型> 变量名(new 类型)

例:
auto_ptr str(new string(“我是小白, 我要做大神”));
auto_ptr a_vtor(new vector(10));

#include 
#include 

using namespace std;

//auto_ptrt(new test()); 忠告1: 智能指针不要定义为全局变量

class test {
public:
	test() {
		cout << "调用构造函数" << endl;
	}
	~test() {
		cout << "调用析构函数" << endl;
	}
	void print() {
		cout << "调用打印函数" << endl;
	}
};

void test_demo() {

	auto_ptr<test>t(new test());

	//忠告2: 不要定义指向智能指针对象的指针变量

	//忠告3: 除非自己知道后果,不然不要把auto_ptr智能指针赋值给同类型的另外一个智能指针

	//在使用智能指针访问对象时,使用方式和普通指针一样
	t->print();
	test* tmp = t.get();
	tmp->print();

	//release取消智能指针对动态内存的托管,之前分配的内存必须手动释放
	test* p = t.release();
	delete p;

	//reset重置智能指针托管的内存地址,如果地址不一致,原来的会被析构掉
	t.reset();
	t.reset(new test());
}
int memory_demo() {

	auto_ptr<test> t(new test());

	//程序执行一段复杂的逻辑,假设尝试从一个必须存在的文件中读取某些数据,而文件此时不存在
	{
		throw exception("文件不存在");
	}
	return 0;
}
int main(void) {

	test_demo();

	try
	{
		memory_demo();
	}
	catch (const std::exception&e)
	{
		cout << e.what() << endl;
	}

	system("pause");
	return 0;
}

C++指针(含智能指针)_第8张图片
使用建议:
1.尽可能不要将auto_ptr变量定义为全局变量或指针;
2.除非自己知道后果,不要把auto_ptr智能指针赋值给同类型的另一个智能指针;
3.C++后auto_ptr已经被"抛弃",已使用unique_ptr替代;

unique_ptr使用详解(C++11)

auto_ptr是用于C++11之前的智能指针,由于auto_ptr基于排查所有模式: 两个指针不能指向同一个资源,复制或赋值都会改变资源的所有权,auto_ptr主要有两大问题:

  1. 复制或赋值会改变资源的所有权,不符合人的直觉;
  2. 在STL容器中使用auto_ptr存在重大风险,因为容器内的元素必须支持可复制和可赋值;
  3. 不支持对象数组的操作;
    所以C++11用更严谨的unique_ptr取代了auto_ptr

unique_ptr特性
基于排查所有权模式: 两个指针不能指向同一个资源

无法进行左值unique_ptr复制构造,也无法进行左值复制赋值操作,但允许临时右值赋值构造和赋值;

保存指向某个对象的指针,当它本身离开作用域时会自动释放它指向的对象;

在容器中保存指针是安全的;

#include 
#include 
#include 

using namespace std;

int main(void) {

	//弊端1 :auto_ptr被C++11抛弃的主要理由 p1=p2,复制或赋值都会改变资源的所有权
	//unique_ptr如何解决这个问题? 不允许显示的右值赋值和构造

	unique_ptr<string>p1(new string("赵云"));
	unique_ptr<string>p2(new string("吕布"));

	cout << *p1.get() << endl;
	cout << *p2.get() << endl;

	//如果一定要转移,使用move把左值转成右值
	p1 = move(p2);
	cout << *p1.get() << endl;
	cout << p2.get() << endl;//这个对象就会为空

	//p1=p2;  左值赋值禁止
	unique_ptr<string>p3(new string("刘备"));
	unique_ptr<string>p4(move(p3)); //左值拷贝构造也不行,必须转成右值

	//弊端2: 在STL容器中使用auto_ptr存在重大风险,因为容器内的元素必须支持复制和赋值
	vector<unique_ptr<string>>vu;
	unique_ptr<string>p5(new string("曹操"));
	unique_ptr<string>p6(new string("许诸"));

	vu.push_back(move(p3));
	vu.push_back(move(p4));

	cout << "vu[0]=" << vu[0] << endl;//在上面p3已经赋值给了p4所有打印出来的是空
	cout << "vu[1]=" << *vu[1] << endl;

	//vu[0]=vu[1]; //unique_ptr不支持直接赋值,没有风险

	//弊端3: auto_ptr不支持对象数组的内存管理,unique_ptr支持
	//但是unique_ptr支持对象数组的管理
	//auto_ptrai(new int[5]); 不能这样定义

	unique_ptr<int[]>ui(new int[5]);//自动会调用delete[] 函数去释放

	system("pause");
	return 0;
}

C++指针(含智能指针)_第9张图片
构造函数
unique_ptrup;//空的unique_ptr,可以指向类型为Type的对象
unique_ptrup2(new Type());//定义unique_ptr同时指向类型为Type的对象
unique_ptr up ; //空的unique_ptr,可以指向类型为T的数组对象
unique_ptr up1(new T[]) ;//定义unique_ptr,同时指向类型为T的数组对象
unique_ptr up(); //空的unique_ptr,接受一个D类型的删除器d,使用d释放内存
unique_ptr up(new T()); //定义unique_ptr,同时指向类型为T的对象,接受一个D 类型的删除器d,使用删除器d来释放内存

赋值
unique_ptr up1(new int(10));
unique_ptr up2(new int(11));
up1 = std::move(up2);//必须使用移动语义,结果,up1 内存释放, up2 交由up1 管理

主动释放对象
up = nullptr ;//释放up指向的对象,将up置为空
或 up = NULL; //作用相同

放弃对象控制权
up.release(); //放弃对象的控制权,返回指针,将up置为空,不会释放内存

重置
up.reset(…) ; //参数可以为 空、内置指针,先将up所指对象释放,然后重置up的值

交换
up.swap(up1); //将智能指针up 和up1管控的对象进行交换

shared_ptr使用详解(C++11)

熟悉了unique_ptr后,其实我们发现unique_ptr这种排他型的内存管理并不能适应所有情况,有很大的局限,如果需要多个指针变量共享一块内存怎么办?
如果有一种方式,可以记录引用特定内存对象的智能指针数量,当复制或拷贝时,引用计数加1,当智能指针析构时,引用计数减1,如果计数为零,代表已经没有指针指向这块内存,那么我们就释放它,这就是shared_ptr采用的策略;

构造函数:
shared_ptrsp;//空的shared_ptr, 可以指向类型为T的对象
shared_ptrsp1(new T());//定义shared_ptr,同时指向类型为T的对象
shared_ptrsp2;//空的shared_ptr, 可以指向类型为T[的数组对象] C++17后支持;
shared_ptrsp3(new T[]{…});//指向类型为T的数组对象C++17后支持
shared_ptrsp4(NULL,D());//空的shared_ptr,接受一个D类型的删除器,使用D释放内存;
shared_ptrsp5(new T(),D());//定义shared_ptr, 指向类型为T的对象,接受一个D类型的删除器,使用D删除器来释放内存;

初始化:
方式一: 构造函数
shared_ptrup1(new int(10)); //int(10)的引用计数为1
shared_ptrup2(up1); //使用智能指针up1构造up2, 此时int(10)引用计数为2

方式二: 使用make_shared初始化对象,分配内存效率更高
make_shared函数的主要功能是在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr; 用法:

make_shared<类型>(构造类型对象需要的参数列表);
shared_ptrp4=make_shared(2);//多个参数以逗号’,'隔开,最多接受十个
shared_ptrp4=make_shared(“字符串”);

赋值:
shared_ptrup1(new int(10)); //int(10)的引用计数为1
shared_ptrup2(new int(11)); //int(11)的引用计数为1
up1=up2; //int(10)的引用计数减1,计数归零内存释放,up2共享int(11)给up1,int(11)的引用计数为2;

主动释放对象
shared_ptrup1(new int(10));
up1=nullptr; //int(10)的引用计数减1,计数归零内存释放
或者up1=NULL; //作用同上

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

交换
swap(up,up2); //交换up和up2管理的对象,原对象的引用计数不变;
up.swap(up2); //同上;

使用陷阱
shared_ptr作为被管控的对象的成员时,小心因循环引用造成无法释放资源;

#include 
#include 

using namespace std;

class girl;

class boy {
public:
	boy() {
		cout << "boy (构造函数)" << endl;
	}
	~boy() {
		cout << "boy (析构函数)" << endl;
	}
	void set_girl_friend(shared_ptr<girl>& g) {
		girl_friend = g;
	}

private:
	shared_ptr<girl>girl_friend;
};

class girl {
public:
	girl() {
		cout << "girl (构造函数)" << endl;
	}
	~girl() {
		cout << "girl (析构函数)" << endl;
	}
	void set_girl_friend(shared_ptr<boy>& b) {
		boy_friend = b;
	}

private:
	shared_ptr<boy>boy_friend;
};

void use_trap() {
	shared_ptr<girl>sp_girl(new girl());//织女
	shared_ptr<boy>sp_boy(new boy()); //牛郎

	sp_girl->set_girl_friend(sp_boy);
	sp_boy->set_girl_friend(sp_girl);
}

int main(void) {

	use_trap();

	system("pause");
	return 0;
}

C++指针(含智能指针)_第10张图片
运行结果看到,并没有调用析构函数;使用weak_ptr弱指针就可以解决这个问题

weak_ptr使用详解(C++11)

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

#include 
#include 

using namespace std;

class girl;

class boy {
public:
	boy() {
		cout << "boy (构造函数)" << endl;
	}
	~boy() {
		cout << "boy (析构函数)" << endl;
	}
	void set_girl_friend(shared_ptr<girl>& g) {
		girl_friend = g;
	}

private:
	shared_ptr<girl>girl_friend;
};

class girl {
public:
	girl() {
		cout << "girl (构造函数)" << endl;
	}
	~girl() {
		cout << "girl (析构函数)" << endl;
	}
	void set_girl_friend(shared_ptr<boy>& b) {
		boy_friend = b;
	}

private:
	//shared_ptrboy_friend;
	weak_ptr<boy>boy_friend;
};

void use_trap() {
	shared_ptr<girl>sp_girl(new girl());//织女
	shared_ptr<boy>sp_boy(new boy()); //牛郎

	//弱指针
	weak_ptr<girl>wp_girl;//定义一个空的弱指针
	weak_ptr<girl>wp_gir2(wp_girl);//使用共享构造
	wp_girl = sp_girl;//允许共享指针赋值给弱指针

	//使用弱指针不会使shared_ptr指针计数增加
	cout << "sp_girl count:" << sp_girl.use_count() << endl;

	//弱指针不可以访问和解引 wp_girl->set_girl_friend(sp_boy);(*wp_girl).set_girl_friend(sp_boy);
	//在必要时可以转成共享指针
	shared_ptr<girl>sp_girl2 = wp_girl.lock();

	sp_girl->set_girl_friend(sp_boy);
	sp_boy->set_girl_friend(sp_girl);
}

int main(void) {

	use_trap();

	system("pause");
	return 0;
}

C++指针(含智能指针)_第11张图片
这样就可以完美地解决了shared_ptr指针中的对象无法析构的问题了;

智能指针的使用陷阱

1.不要把一个原生指针给多个智能指针管理
int*x=new int(5);
unique_ptrup1(x);
unique_ptrup2(x);
警告: 以上代码使up1 , up2指向同一块内存,非常危险或以下形式:
up1.reset(x);
up2.reset(x);

2.记得使用up.release()的返回值
在调用up.release()时是不会释放up所指向的内存的,这时返回值就是对这块内存的唯一索引,如果没有使用这个返回值释放内存或是保存起来,这块内存就泄漏了;

3.禁止delete智能指针get函数返回的指针
如果我们主动释放掉get函数获得的指针,那么智能指针内部的指针就变成野指针了,析构时造成重复释放,带来严重的后果;

4.禁止用任何类型智能指针get函数返回的指针去初始化另外一个智能指针!
shared_ptrsp1(new int(10));
shared_ptrsp2(sp1.get()); //典型的错误用法

欢迎广大编程爱好者一起分享技术!!
本人学院的资深大神在B站做技术视频分享
C++指针(含智能指针)_第12张图片

你可能感兴趣的:(C++指针(含智能指针))