12-动态内存

12.1 智能指针

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

#include <memory>

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

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

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


shared_ptr 和 unique_ptr支持的操作

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

unique_ptr<T> 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<T>(args);//返回一个shared_ptr,指向一个动态分配的类型为T的对象,使用args初始化此对象

shared_ptr<T> 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<int> *p = new vector<int>{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<int> p(new int(42));//接受指针参数的智能指针的构造函数是explicit的,不可将一个内置指针隐式转换为智能指针,必须进行直接初始化

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


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

shared_ptr<T>p(u);//p从unique_ptr接管对象,u置空

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

shared_ptr<T>p(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<int> p4;

unique_ptr<int> p1(new int(1024));

int *p2=new int(1203);

unique_ptr<int> p3(p2);


unique_ptr特有的操作

unique_ptr<T> u1;//

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

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

unique_ptr<T, D> 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<string> p(p1.release());//p1置空,p管理p1的对象

unique_ptr<string> 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<T> w;

weak_ptr<T> 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<int>(100);

weak_ptr<int> wp(p);

if (shared_ptr<int> 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<int[]> up(new int[10]);

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

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

unique_ptr<T[]> u;

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

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


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

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

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

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

12.2.2 allocator 类

#include<memory>

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


allocator<T> 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);

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容

  • 生存期全局对象:程序启动时分配,程序结束时销毁局部对象:进入其定义所在的程序块时被创建,离开块时被销毁static...
    菜鸡也会飞阅读 232评论 0 2
  • 12动态内存 每个程序分配有静态内存和栈内存,还有一个内存池称为自由空间或堆。用来存储动态分配。 12.1动态内存...
    龟龟51阅读 227评论 0 0
  • 导读## 最近在补看《C++ Primer Plus》第六版,这的确是本好书,其中关于智能指针的章节解析的非常清晰...
    小敏纸阅读 1,977评论 1 12
  • 强类型枚举 枚举:分门别类与数值的名字 枚举类型是C及C++中一个基本的内置类型,不过也是一个有点"奇怪"的类型。...
    认真学计算机阅读 2,663评论 0 3
  • 1. 什么是智能指针? 智能指针是行为类似于指针的类对象,但这种对象还有其他功能。 2. 为什么设计智能指针? 引...
    MinoyJet阅读 632评论 0 1