C++入门到精通 --第四章 智能指针

四、智能指针

01 直接内存管理(new/delete)、创建新工程观察内存泄漏

1 直接内存管理(new/delete)
2 创建新工程,观察内存泄漏

直接内存管理(new/delete)

C语言中是malloc/free

定义初值
int *pointi = new int(100);
string *points = new string(5, 'a');	//"aaaaa"
vector<int> *pointv = new vector<int>{1, 2, 3, 4, 5};  

概念 “值初始化”:用()来初始化

int *pointi = new int();
auto pointi_t = new auto(pointi);
//前面的auto推断出的是int **

推荐在delete后将该指针设置成nullptr
C++中出现了智能指针,会帮助程序猿收回内存。

创建新工程,观察内存泄漏

MFC应用程序能够在一定程度上(程序推出的时候),帮助发现内存泄漏。
MFC:微软公司出的基础的程序框架(MFC基础类库),可以生成一个带窗口(带界面)的程序框架,MFC简化了很多界面开发工作。

快捷键 ctrl + ] 跳转到对应的{}

02 new、delete探秘,智能指针概述、shared_ptr基础

1 new/delete探秘

new/delete是什么
operator new()和operator delete()
基本new如何记录分配的内存大小供delete使用
申请和释放一个数组
为什么new/delete、new[]/delete[]要配套使用

2 智能指针总述
3 shared_ptr基础

常规初始化(shared_ptrnew配合)
make_shared函数

new/delete探秘

new/delete是什么?

new/delete类对象时,有初始化/释放的能力(调用构造函数/析构函数),这些是malloc/free所不能的。

operator new()和operator delete()

new/delete是操作符,operater new()和operator delete()是函数

  • new简单理解为做了两件事:a)分配内存(实际上通过operator new());b)给内存初始化
  • delete也做了两件事:a)反初始化(调用析构函数);b)释放内存(operator delete())
基本new如何记录分配的内存大小供delete使用

不同编译器new内部有不同的实现方式,new内部有记录机制,记录分配出多少内存。

申请和释放一个数组

int *p = new int[2];
delete[] p;
如果申请一个类数组,会在内存中存入该数组中类的个数,以便与调用等量的析构函数。

智能指针总述

  • 裸指针:直接用new返回的指针,强大灵活,但是需要开发者全程维护。
  • 智能指针:理解为“裸指针”进行了包装,能够“自动释放所指向的对象内存”。C++标准库std提供了四种智能指针:
    • 都是类模版
      • auto_ptr(C++98)已经被unique_ptr取代
      • unique_ptr(C++11):独占式指针。同一时间内,只有一个指针指向该对象
      • shared_ptr(C++11):共享式指针。多个指针指向同一个对象,最后一个指针被销毁时,这个对象会被销毁
      • weak_ptr(C++11)用来辅助shared_ptr
  • 用来帮助我们进行动态分配对象(new出来的对象)的生命周期的管理。能够有效防止内存泄漏

shared_ptr基础

常规初始化(shared_ptr和new配合)

每个share_ptr的拷贝都指向相同的内存

工作原理引用计数。只有最后一个指向该内存的shared_ptr不需要再指向该对象时,才会析构这个对象。计数的作用是每增加一个shared_ptrcount + 1,销毁指针到count为0时,就会释放内存。

std::shared_ptr<int> pi(new int(100));	
//智能指针explicit不允许隐式转换
//故std::shared_ptr<int> pi = new int;是错误的

make_share函数:标准库里的函数模版

在自定义删除器时会受到限制

shared_ptr<int> pt = make_shared<int>(100);

03 shared_ptr常用操作、计数、自定义删除器

1 shared_ptr引用计数的增加和减少

引用计数的增加
引用计数的减少

2 shared_ptr指针常用操作

use_count()
unique()
reset()
*解应用
get()
swap()
=nullptr
智能指针名字作为判断条件
指定删除器以及数组问题

shared_ptr引用计数的增加和减少

每个shared_ptr都会记录有多少个其他的shared_ptr指向相同的对象。

shared_ptr指针常用操作

  • use_count()返回多少个智能指针指向某个对象,主要用于调试目的
  • unique()是否该智能指针独占某个指向的对象,如果是,返回True
  • reset()复位/重置
    • 不带参数时
      • 若该指针是唯一指向该对象的指针,那么释放该对象,并将该指针置空(nullptr)
      • 若该指针不是唯一指向该对象的指针,那么不释放该对象,但计数减一,该指针置空(nullptr)
    • 带参数时(一般是new出来的指针)
      • 若该指针是唯一指向该对象的指针,那么释放该对象,并将该指针指向新对象
      • 若该指针不是唯一指向该对象的指针,那么不释放该对象,但计数减一,该指针指向新对象
  • *解应用获得指针指向的对象
  • get()返回保存的指针(裸指针)。用于有些函数的参数需要的是一个内置裸指针而不是智能指针
  • swap()交换两个智能指针所指向的对象
  • =nullptr
    • 将所指向的对象引用计数减一,若引用计数变为0,则释放该指针指向的对象
    • 将智能指针置空
  • 指定删除器以及数组问题
    • 可以指定自己的删除器函数取代系统的默认删除器
    • 管理动态数组时
//定义一个删除器函数
void deletefunc(int *p)
{
	delete []p;
}
shared_ptr<int> pt(new int[100], deletefunc);

//打包
template<typename T>
shared_ptr<T> make_array(size_t n)
{
	return shared_ptr<T>(new T[n], default_delete<T[]>());
}
shared_ptr pt = make_array<int>(3);	//长度为3的整形数组,自定义删除器

//lambda函数做自定义删除器
shared_ptr<int> pt(new int[10], [](int *p) {
	delete []p;
	}

//default_delete做删除器
shared_ptr<int> pt(new int[10], default_delete<int[]>());
	
//C++17
shared_ptr<int[]> pt(new int[10]);	//在<>中加[]

size_t 源码中 -> typedef unsigned __int64 size_t;

  • 32位系统的size_t是4字节的unsigned int
  • 64位系统的size_t是8字节的unsigned long long

weak_ptr概述、weak_ptr常用操作、尺寸

1 weak_ptr概述

weak_ptr的创建

2 weak_ptr常用操作

use_count()
expired()
reset()
lock()

3 尺寸问题

weak_ptr概述

weak_ptr用来辅助shared_ptr进行工作。这个智能指针指向一个由shared_ptr管理的对象。但是它并不控制所指向对象的生命周期(不会改变shared_ptr的引用计数)。shared_ptr能够照常释放所指向的对象。

weak_ptr的创建

一般都用shared_ptr初始化。

auto pi = make_shared<int>(100);
weak_ptr<int> pwi(pi);	
//不会改变强引用(strong ref)的引用计数,但是会增加弱引用(weak ref)计数

weak_ptr常用操作

  • use_count获取当前所观测资源的强引用计数
  • expired()是否过期,用来判断所观测资源是否被释放
  • reset()将该弱引用指针设置为
  • lock():功能是检查weak_ptr所指向的对象是否存在。如果存在返回一个指向该对象的shared_ptr,如果不存在则返回一个空的shared_ptr
//接着
auto pi2 = pwi.lock();	//会使强引用加1,pwi仍是弱引用

尺寸问题

weak_ptr的尺寸和shared_ptr一样大,是裸指针的2倍(8字节)。实际上在它们内部包含了两个指针。

05 shared_ptr使用场景、陷阱、性能分析、使用建议

1 std::shared_ptr使用场景
2 std::shared_ptr使用陷阱分析
3 性能说明

尺寸问题
移动语义

std::shared_ptr使用陷阱分析

  • 慎用裸指针
    • 当把一个普通裸指针绑定到了一个shared_ptr上之后,内存管理的责任就交给了智能指针,这时就不应该再用裸指针访问shared_ptr指向的内存了
    • 不能用裸指针初始化多个shared_ptr,会导致多个智能指针之间不关联,删除器会由于两次释放相同内存而报错
  • 慎用get()返回的指针
    • get返回的指针不能delete,否则会异常
    • 不能将其他智能指针绑到get返回的指针上
  • 不要把类对象指针(this)作为shared_ptr返回,改用enable_shared_from_this(工作中需要返回该调用函数本身的类的智能指针时使用)
    • 工作原理:enable_shared_from_this中又一个弱指针weak_ptr,这个弱指针能够监视this,调用shared_from_this时,实际上调用了这个weak_ptr的lock方法
class CT : public enable_shared_from_this<CT>
{
public:
	shared_ptr<CT> getself()
	{
		return shared_from_this(); //这个是enable_shared_from_this类模板中的方法
	}
};

性能说明

尺寸问题

shared_ptrweak_ptr均占8个字节,也就是两个指针,一个指针指向该指向对象的智能指针;一个指针指向控制块(包括强引用计数、弱引用计数、删除器、分配器等)

移动语义

移动构造一个新的智能指针

shared_ptr<int> p1(new int(100));
shared_ptr<int> p2(std::move(p1));

会将p1指向的对象移动给p2,p1不再指向该对象(变成空指针)。

06 unique_ptr概述、常用操作

1 unique_ptr概述

常规初始化
make_unique函数

2 unique_ptr常用操作

unique_ptr概述

独占式的概念(专属所有权):同一个时刻只能有一个unique_ptr指向一个对象。当被销毁时,所指向的对象同时也被销毁。

unique_ptr<int> pt(new int(103));

//c++14:make_unique
unique_ptr<int> pt = make_unique<int>(104);
auto pt = make_unique<int>(105);

unique_ptr常用操作

  • 移动语义
unique_ptr<string> ps1(new string("ingenious"));
unique_ptr<string> ps2(std::move(ps1));
  • release():放弃对指针的控制权,切断指针和对象之间的联系。返回裸指针,并将该智能指针置空,该裸指针需手动释放
unique_ptr<string> ps1(new string("ingenuous"));
unique_ptr<string> ps2(ps1.release());
  • reset()
    • 不带参数:释放智能指针指向的对象,并把指针置空
    • 带参数:释放智能指针指向的对象,并将智能指针指向括号内的新对象
unique_ptr<string> ps3(new string("fractious"));
ps3.reset(ps1.release());
  • = nullptr:释放指向的对象并置空该智能指针
  • get():返回智能指针中保存的裸指针
  • swap():交换两个指针所指向的对象
  • 转换成shared_ptr形式:如果unique_ptr为右值就可以转换成shared_ptr形式
auto func()
{
	return unique_ptr<string> st(new string("fd"))//返回的是临时对象,是右值
}

shared_ptr<string> p = func();

07 返回unique_ptr、删除器、尺寸、智能指针总结

1 返回unique_ptr
2 指定删除器
3 尺寸问题
4 智能指针总结

指定删除器

格式: unique_ptr<指向的对象类型, 删除器类型> 指针名

void mydelete(string* p)
{
	delete p;
	p = nullptr;
}

//typedef void (*pr) (string *);
//using pr = void (*) (string *);
typedef decltype(mydelete)* pr;
unique_ptr<string, pr> point(new string("hei"), mydelete); 

unique_ptr自定义删除器需要在模板参数中加入删除器函数的类型,例中提供了用函数指针做模板参数的情况。

尺寸问题

通常情况下,unique_ptr和裸指针的大小一样(4字节)。但是自定义删除器就可能增加所占用的内存。

总结

  • 智能指针的设计目的:帮助释放内存,以防内存泄漏
  • auto_ptr为什么被废弃?
    • 不能再容器中保存
    • 不能从函数中返回auto_ptr
    • C++11提出的unique_ptr相比C++98的auto_ptr更安全
    • … …
发布了6 篇原创文章 · 获赞 11 · 访问量 852

猜你喜欢

转载自blog.csdn.net/qq_44455588/article/details/104392589