众所周知,C++五大内存区:全局数据区(静态区)、代码区、栈区、堆区、常量区。
全局数据区(静态区):存放全局变量,静态数据和常量;
代码区:存放所有类成员函数和非成员函数代码,函数体的二进制代码。
栈区:存放为运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
堆区:new、malloc、realloc分配的内存块,一般编译器不会释放内存,需要用程序释放,内存泄露通常说的就是堆区。
常量区:存放常量的,不允许修改。
其中,堆区就是动态内存的区域,就是程序员自己分配和释放内存的区域。
目录
1.智能指针
1.1 介绍
头文件:#include<memory>
智能指针是模板。
shared_ptr:共享指针,允许多个指针指向同一个对象。
unique_ptr:独占指针,只允许一个指针指向一个对象。
weak_ptr:弱引用,指向shared_ptr所管理的对象。
特点:除weak_ptr外,智能指针可以在本身被销毁的时候自动释放所指向的对象的内存。
1.2 智能指针使用陷阱(☆☆☆☆☆)
1.不使用相同的内置指针值初始化(或reset)多个智能指针——多个智能指针是独立的,计数器为0时,会对同一片动态内存区多次delete。
2.不delete get()返回的指针——若delete了get()返回的指针,则当智能指针销毁时,会再次调用delete,同一片动态内存区多次delete。
3.不使用get()初始化或reset另一个智能指针——两个智能指针是独立的,计数器为0时,会对同一片动态内存区多次delete。
4.如果你使用get()返回的指针,记住当最后一个对应的智能指针销毁后,你的指针就无效了
5.如果你使用智能指针管理的资源不是new分配的资源,记住传递给它一个deleter删除器——不是new分配的资源内存则不在动态内存里,那么如果不传递一个deleter删除器,则智能指针会调用delete释放一个未定义的内存区。
6.有些类不具有良好定义的析构函数,则使用智能指针时需要提供删除器,来确保资源能被正确释放。——如:
比如网络库里有个结构体connection,其没有析构函数,
有个disconnect的函数可以释放connection结构体的资源。
那么当我们用智能指针来管理connection的话,就必须提供disconnect的删除器。
struct connection;
struct destination; //连接所需信息
void end_connection(connection *p) { disconnect(*p); }
void f(destination &d)
{
connection c=connect(&d);
shared_ptr<connection> p(&c,end_connection);
或
unique_ptr<connection,decltype(end_connection)*> p(&c,end_connection);
}
1.2 shared_ptr
1.2.1 特点及操作
形式:shared_ptr<T> ptrname;
特点:每个shared_ptr都有一个关联的计数器,来记录有多少个智能指针指向这个对象,
我们拷贝一个shared_ptr,计数器会递增,如:
1.当用一个shared_ptr初始化另一个shared_ptr
2.当把一个shared_ptr作为参数传递给一个函数
3.当把一个shared_ptr作为一个函数返回值时
我们销毁一个shared_ptr,或给一个shared_ptr赋值时,计数器会递减,如:
1.局部shared_ptr离开一个作用域,会被销毁,则指向这个对象的其它shared_ptr指针的计数器会递减
当计数器为0时(即没有智能指针指向这个对象了),将会调用这个对象的析构函数或delete来销毁它,并释放内存。
T:是一个类型,可以是内置类型、容器等等。
操作 | 含义 |
shared_ptr<T> sp | 空智能指针,指向类型为T的对象 |
sp | 将sp作为条件判断,如果sp指向一个对象,则为true,否则为false |
*sp | 解引用sp,获得它指向的对象 |
sp->mem | 等价于(*sp)->mem,调用sp指向对象的成员 |
sp.get() | 返回sp中保存的指针,返回的是一个内置指针。 使用场景:我们需要向不能使用智能指针的代码传递一个内置指针。 注意:若智能指针释放了其对象,返回的指针所指向的对象也就消失了,并且get返回的指针的代码,不能delete此指针。 |
swap(sp1,sp2) sq.swap(sp2) |
交换两个智能指针 |