12-动态内存

12.1 智能指针

智能指针行为类似普通指针,但它负责自动释放所知的对象。

#include

shared_ptr : 允许多个指针指向同一个对象,每个指针都会记录有多少个其他指针指向相同的对象

unique_ptr : 某个对象只允许一个指针指向它

weak_ptr : 弱引用的伴随类,指向shared_ptr所管理的对象。


shared_ptr 和 unique_ptr支持的操作

shared_ptr sp;//空指针,可以指向类型为T的对象

unique_ptr up;

if (p){}//若p指向一个对象,条件为true

*p;//解引用p,获取其指向的对象

p->mem;//等价于(*p).mem

p.get();//返回p中保存的指针,与对象的引用计数无关,不要使用它来初始化一个智能指针或赋值

swap(p,q);//交换p,q中的指针

p.swap(q);


shared_ptr独有的操作

make_shared(args);//返回一个shared_ptr,指向一个动态分配的类型为T的对象,使用args初始化此对象

shared_ptr p(q);//p是q的拷贝;递增q中的引用计数,q中的指针必须可转换为T*

p = q;//pq保存的指针需可以互相转化,递减p的引用计数,递增q的引用计数;当p的引用计数为0,会将管理的原内存释放

p.use_count();//返回与p共享的对象的智能指针个数。

p.unique();//若p.use_count()返回1,则返回true。


make_shared函数会在动态内存中分配一个对象并初始化。

传递的参数必须符合类型的某个构造函数;内置类型若不提供参数,将进行值初始化。

shared_ptr的拷贝auto p(q);

拷贝操作将会递增引用计数的值:

1,用一个shared_ptr初始化另一个shared_ptr

2,将其作为参数传递给一个函数

3,作为函数的返回值

shared_ptr的赋值和销毁,r = q

1,赋值

2,shared_ptr被销毁

以上操作会递减指针引用计数的值,当计数值为0时释放所管理的对象(shared_ptr的析构函数)

12.1.2 直接管理内存

int *pi = new int;//内置类型和组合类型的对象的值未定义,默认初始化

string *ps = new string;

int *pi = new int(1024);//直接初始化

string *ps = new string(10,'9');

vector *p = new vector{0,1,2,3,4,5,6,7,8,9};

int *pi = new int();//值初始化

string *ps = new string();

初始化器

auto p1= new auto(obj);//根据obj对象推断类型,并用obj初始化,obj只可以拥有一个

动态分配const对象

const int *p=new const int(1024);//需初始化

const string *p = new const string;

int *p=new int;//分配失败,抛出std::bad_alloc

int *p=new (nothrow) int;//分配失败,返回空指针

delete p;//销毁给定指针指向的对象,释放对应的内存

p=nullptr;//重置指针,避免成为空悬指针

释放非new分配的内存或多次释放行为未定义。

12.1.3 shared_ptr和new结合使用

shared_ptr p(new int(42));//接受指针参数的智能指针的构造函数是explicit的,不可将一个内置指针隐式转换为智能指针,必须进行直接初始化

默认情况下,初始化智能指针的普通指针必须指向动态分配的内存(使用delete释放),但可以提供其他代替delete的操作来将智能指针绑定到指向其他类型的指针上。


shared_ptr p(q);//p管理内置指针q(new分配)所指的对象,q可转换为T*

shared_ptrp(u);//p从unique_ptr接管对象,u置空

shared_ptrp(q, d);//p结构内置指针q指向的对象,使用可调用对象d来代替delete

shared_ptrp(p1,d);//p是p1(shared_ptr)的拷贝,但使用d代替delete 

p.reset();

p.reset(q);

p.reset(q,d);

若p是唯一指向其对象的shared_ptr,reset会释放此对象,将p置空;若传递了内置指针q,则令p指向q;若还有参数d,会调用d来释放q;会更新引用计数。

共享对象的智能指针的处理:

if (!p.unique()){

p.reset(new string(*p));//p不是唯一指向对象的指针,但此时要改变p指向的元素,必须创建一个拷贝

}

//对p的对象进行操作


12.1.4 智能指针和异常

1,不使用相同的内置指针初始化(或reset)多个智能指针

2,不delete get()返回的指针

3,不使用get() 初始化或reset另一个智能指针

4,使用get()返回的指针,当其对应的最后一个智能指针销毁后,get()返回的指针无效了

5,使用智能指针管理不是有new分配的内存,需要有附加的删除器

12.1.5 unique_ptr

某个时刻只有一个unique_ptr指向一个对象,unique_ptr被销毁时,对象也被销毁。

定义的同时必须初始化,必须采用直接初始化。不支持拷贝和赋值

unique_ptr p4;

unique_ptr p1(new int(1024));

int *p2=new int(1203);

unique_ptr p3(p2);


unique_ptr特有的操作

unique_ptr u1;//

unique_ptr u2;//u2使用类型为D的可调用对象释放指针

unique_ptr u(d);//使用类型为D的对象代替delete

unique_ptr u(p, d);//使用普通指针p初始化u,销毁时使用D类型的对象代替delete

u = nullptr;//释放u所指的对象,将u置空

u.release();//u放弃对指针的控制权,返回指针,并将u置空;可用来初始化另一个智能指针或赋值

u.reset();//释放u所指的对象

u.reset(q);//释放u所指的对象,u指向这个内置指针绑定的对象

u.reset(nullptr);//释放u所指的对象,将u置空;

release和reset可以将对象的所有权转移到另一个unique_ptr上。

unique_ptr p(p1.release());//p1置空,p管理p1的对象

unique_ptr p2(new string("haha"));

p.reset(p2.release());//p释放了原指向的内存,重新指向了p2的内存,p2为空


可以拷贝或赋值一个将要被销毁的unique_ptr,比如从函数返回一个unique_ptr或返回局部unique_ptr的拷贝;

12.1.6 weak_ptr

和某些shared_ptr共享同一个对象,但不会增加shared_ptr的引用计数。weak_ptr不会控制对象的生存期。


weak_ptr w;

weak_ptr w(sp);//w和sp(一个shared_ptr)指向相同的对象,T必须是可以转换为sp指向的类型。

w = p;//p可以是shared_ptr或weak_ptr,赋值后w,p共享对象

w.reset();//将w置为空

w.use_count();//与w共享对象的shared_ptr数量

w.expired();//若w.use_count()为0,则为true

w.lock();//若w.expired()返回true,则返回空的shared_ptr;否则返回一个w指向对象的shared_ptr


不可用weak_ptr直接返回对象,必须用lock;

auto p = make_shared(100);

weak_ptr wp(p);

if (shared_ptr np = wp.lock()){

//np和p共享同一个对象

}

12.2 动态数组

使用new T[] 和delete[]

T *p = new T[n];//n必须是整型,不必是常量。n可以为0,返回合法的非空指针。

typedef int  arrInt[100];

int *p = new arrInt;

注意p是元素的指针而不是数组的指针,并且严格说动态数组非数组,只是一段有类型的连续的存。

默认情况下,创建的动态数组执行默认初始化;

int *p1 = new int[10];//值未定义,默认初始化

int *p2 = new int[10]();//值初始化,0

直接初始化

int *p3 = new int[10]{1,2,3,4,5,6,7,8};

delete [] p1;//添加[]和去掉[],需要看p1是单个元素的指针还是动态数组的指针,否则delete操作未定义


可直接使用unique_ptr管理动态数组

unique_ptr up(new int[10]);

up.release();//自动调用delete[]销毁

指向数组的unique_ptr不支持成员访问运算符。

unique_ptr u;

unique_ptr u(p);//内置指针p指向动态数组,p必须可以转换为类型T*

up[i];//需使用下标来访问


shared_ptr不支持直接管理动态数组,但可以定义自己的删除器,间接管理。

shared_ptr sp(new int[10], [] (int *p){ delete [] p;});

sp.reset();//使用定义时的lambda作为删除器

sp.get();//借用此指针访问动态数组的值

12.2.2 allocator 类

#include

allocator类将内存分配和对象构造分离出来,类型感知的内存分配方法,分配的内存是原始的,未分配的。


allocator a;//a可以为类型为T的对象分配内存

auto p = a.allocator(n);//分配n个未经构造的内存,保存n个类型为T的对象

a.construct(p, args);//p是一个类型为T*的指针,指向一块原始内存;args被传递给类型为T的构造函数,args为参数列表

a.destory(p);//p为类型为T*的指针,对p所指对象执行析构函数,必须是构造过得

a.deallocator(p,n);//p是由allocator返回的指针,n是创建时的大小;释放从p开始的n个类型为T的对象,在此之前必须为每个内存调用destory;


拷贝和填充未初始化内存

uninitialized_copy(b,e,b2);//将范围[b,e)的元素拷贝到b2指向的未构造的内存

uninitialized_copy_n(b,n,b2);//

uninitialized_fill(b,e,t);//在[b,e)指向的原始内存开始创建n个对象,对象的值均为t的拷贝

uninitialized_fill_n(b,n,t);

返回指向最后一个构造的元素的下一位置。

你可能感兴趣的:(12-动态内存)