C++中的动态内存

来源:互联网 发布:电脑事件提醒软件 编辑:程序博客网 时间:2024/06/03 01:41

动态内存

静态内存用来保存局部static对象、类static数据成员以及定义在任何函数之外的变量。栈内存用来保存定义在函数内的非static对象。栈内存或者静态内存中的对象由编译器自动创建和销毁。对于栈对象,仅在其定义的程序块运行时才存在;static对象在使用之前分配,在程序结束时销毁。除了静态内存和栈内存之外,程序还拥有一个称作自由空间或者的内存池。程序利用堆来存储动态分配的对象。动态对象的生存期由程序来控制,与其在何处创建无关,只有显式的释放时才会被销毁。

[#知识点:全局对象在程序加载时进行分配,在程序结束时销毁,全局对象、静态对象(注意两者的区别,全局对象可以作用于多个文件而静态对象只能作用于本文件)保存在静态区,一般有系统自动释放;局部自动对象在程序进入其所在的定义区进行块时创建,在离开该作用区域时销毁,局部对象(函数内的非静态对象一般存储于栈区),一般由系统自动释放;动态对象有程序来分配,并由程序释放,一旦没有主动释放,这块内存将被认为泄露了。#]

使用动态内存的原因:
1) 程序不知道自己需要使用多少对象;
2) 程序不知道所需对象的准确类型;
3) 程序需要在多个对象之间共享数据。

new与delete

动态内存的管理是通过一对运算符来完成:new、delete。new在动态内存中为对象分配空间并返回一个指向该对象的指针;delete接受一个动态对象的指针销毁该对象并释放内存。

new

自由空间分配的内存是无名的,因此new无法为其分配的对象命名,而是返回一个指向该对象的指针。
默认情况下,动态分配的对象是默认初始化的,因此此时内置类型和符合类型的对象的值是未定义的(注意此时虽然称为默认初始化,但并没有对其进行初始化),而类类型对象将用默认构造函数来进行初始化。
可以使用auto(obj)来推断类型,但有一点值得注意的是,new之后接上auto构成的初始化器,会将obj对象的值作为新分配的对象的值。(可否使用decltype?
new可以创建const对象。
new也能返回void* 指针,当内存分配失败的时候。此种new操作称为定位new
// T *name = new T;int *pi = new int; //pi指向一个动态分配,未初始化的无名对象。string *ps = new string; //ps指向一个动态分配的空string// T *name = new T(args);int *pi = new int(0); //pi指向一个动态分配,初始为0的对象。string *ps = new string(3, 'g'); //ps指向一个动态分配的初始化为"999"stringvector<int> *pv = new vector<int>  {0, 1, 2}; //pv指向一个动态分配的初始化为{1,2 3}的vector

delete

为了防止动态内存耗尽,在动态内存使用完后应该进行释放以归还给系统。delete接受一个指向动态分配对象的指针以释放对象。有的机器在对指针指向的动态内存进行delete之后,仍旧会继续保持被释放的内存的地址。
delete执行的是连个操作:销毁对象和释放内存。new可以开辟内存空间,但对内置类型或者复合类型并不进行初始化。
释放一个并非new分配的内存或者将相同的指针释放多次,其行为是未定义的。
delete之后,指针就变成了空悬指针,即指向一块曾经保存数据对象但现在已经失效的内存的指针。空悬指针有着和未初始化的指针相类似的缺点。最好在释放内存之后,对指针重置,即将其变为空指针。可以使用nullptr来对空悬指针进行赋值。
当多个指针指向相同的内存时,delete一个指针就可以释放内存,使其他指针都变为空悬指针,但重置一个指针并不会作用于其他指针。  
delete p;//p是一个指向动态对象的指针或者空指针

智能指针

当使用动态内存时:如果忘记释放内存,容易造成内存泄露;如果过早释放,容易造成野指针。为了更容易的使用内存,新标准提供了两种智能指针负责自动释放动态内存:shared_ptr、unique_ptr。除此之外还定义了一种弱指针:weak_ptr。三种类型均定义在memory头文件中。

shared_ptr类

1、定义
类似于vector,智能指针也是模版,创建智能指针时需提供额外的信息。 
shared_ptr<string> p1;

2、初始化

3、操作
最安全的分配和使用动态内存的方式就是调用make_shared的标准函数,结合auto可以更方便地使用。
拷贝或者赋值时,shared_ptr会记录有多少个其他的shared_ptr指向相同的对象,这样的计数器称之为引用计数。当shared_ptr被拷贝时,与其关联的引用计数就会递增;当shared_ptr被赋值或者销毁时,其引用计数会递减。(这样的引用计数是如何通知给每一个指向相同的对象的智能指针的?
当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr类会自动通过析构函数来销毁此对象。析构函数一般用来释放对象所分配的资源。
shared_ptr不直接支持管理动态数组,但可以同多自己定义删除器来实现。因为默认情况下,共享智能指针调用delet来销毁对象。
share_ptr为定义下标运算符,而且智能指针类型不支持指针算术运算。
shared_ptr<T> sp  //指向类型为T的空智能指针*p  //解引用p  //将p作为一个判断条件,若p指向一个对象则返回truep->mem  //等价于(*p).memp.get()  //返回p中保存的指针,swap(p, q)  //交换p和q中的对象p.swap(q)  //交换p和q中的对象//shared_ptr特有的操作make_shared<T>(args)  //返回一个指向类型为T的对象的share_ptr,并用args初始化对象。shared_ptr<T> p(q)  //p是q的拷贝,会增加q中的计数器且q中的指针必须能转换为 T* 。p = q  //此操作会递减p的引用计数,递增q的引用计数。若p的计数为0,则释放内存。p.unique()  //若p.use_count() 的值为1,则返回true。p.use_count()  //返回与p共享对象的智能指针的数量。//shared_ptr和new的结合使用shared_ptr<T> p(q)  //p管理内置指针q所指向的对象,q必须指向new分配的内存,且能够转换为T*类型shared_ptr<T> p(u)  //p从unique_ptr处接管了对象的所有权,将u置为空shared_ptr<T> p(q, d)  //p管理内置指针q所指向的对象,q必须指向new分配的内存,且能够转换为T*类型。p将使用可调用对象d来代替deleteshared_ptr<T> p(p2, d)  //p是shared_ptr p2的拷贝,p将用可调用对象d来代替delete。p.reset()  //若p是唯一指向其对象的shared_ptr,reset会释放掉此对象。p.reset(q)  //若传递了可选的参数内置指针q,则会令p指向q,否则会置p为空。p.reset(q, d)  //若传递了参数q和d,则会调用d来释放对象。

unique_ptr类

与shared_ptr不同的是,每个时刻只能有一个unique_ptr指向一个给定的对象。当unique_ptr被销毁时,对象也被销毁。
由于unique_ptr拥有所指定的对象,因此不能执行拷贝和 赋值操作。但有一个例外,就是在该指针即将被销毁时可以进行拷贝和赋值操作,例如返回一个unique_ptr。
可以使用相应的release和reset方法将其对资源的拥有权转移给另一个。release函数会切断智能指针对内存的拥有权但并不会释放内存,一旦该内存没有被其他指针接管,那么该内存就相当于被泄露了。
[##当指针的类型是数组时,标准库提供了可以管理new分配数组的unique_ptr版本,使用release函数会自动释放动态数组对象。但此时的指针并不能执行点和箭头运算符,却可以使用下表运算符来对数组中的元素进行访问。##]
没有与make_shared类似的库函数来返回一个unique_ptr,所以定义一个unique_ptr时,需要将其与new创建的指针进行绑定。类似于shared_ptr,初始化时必须使用直接初始化。
unique_ptr<T> up  //指向类型为T的空智能指针*p  //解引用p  //将p作为一个判断条件,若p指向一个对象则返回truep->mem  //等价于(*p).memp.get()  //返回p中保存的指针,swap(p, q)  //交换p和q中的对象p.swap(q)  //交换p和q中的对象//unique_ptr特有的操作unique_ptr<T> u1  //空指针unique_ptr<T, D> u2  //u2使用类型为D的对象来释放它的指针unique_ptr<T, D> u(d)  //用类型为D的对象d来代替deleteu = nullptr  //释放u指向的对象u.release()  //u放弃对指针的控制权,返回指针,并将u置为空u.reset()  //释放u指向的对象u.reset(q)  //令u指向内置指针q的对象u.reset(nullptr)  

weak_ptr类

weak_ptr类是一种不控制所指向对象生存期的智能指针,指向由一个shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变其引用计数。
当定义一个weak_ptr时,要用一个shared_ptr来初始化它。(是否可以使用弱指针来对内存进行释放?
因为weak_ptr指向的对象可能被shared_ptr释放掉了,因此很可能用该弱指针访问对象时发生错误。
weak_ptr<T> wp  //空指针weak_ptr<T> wp(sp)  //与sp指向相同对象的weak_ptrw=p  //p为shared_ptr或者weak_ptrw.reset()  //将w置空w.use_count()  //与w共享对象的shared_ptr的数量w.expired()  //若w.use_count()为0,返回truew.lock()  //如果expired为true,返回一个空的shared_ptr,否则返回一个指向w的对象的shared_ptr  

动态数组

new和delete运算符一次分配和释放一个对象。当需要一次为很对象分配内存时,C++提供了两中一次分配一个对象数组的方法:新的new表达式语法、allocator类。
可以参考:C++:关于动态数组

0 0
原创粉丝点击