C++11动态内存

C++11动态内存,第1张

  • 如果在一个条件判断中使用智能指针,效果就是检测它是否为空。

shared_ptr 和 unique_ptr都支持的 *** 作

  • shared_ptr sp

  • unique_ptrsp

  • 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_ptrp(q) p是shared_ptr q的拷贝;此 *** 作会递增q中的计数器。q中的指针必须能转换为T*
  • p=q 两个都是shared_ptr ,所保存的指针必须能相互转换。此 *** 作会减少p的引用计数,增加q的引用计数。如果p的引用计数达到0,则将其管理的内存释放
  • p.unique() 若p.use_count()为1,返回true,否则false
  • p.use_count() 返回与p共享对象的智能制造的数量;可能很慢,主要用于调试。

delete只能释放一块动态分配的内存,或者空指针。释放一块非new分配的内存或者将相同的指针释放多次,其行为都是未定义的。

虽然一个const对象的值不能被改变,但是它本身是可以被销毁的。

shared_ptr与new结合使用

可以用new返回的指针来初始化智能指针

shared_ptrp2(new int (42));

注意:接受指针参数的智能指针的构造函数是explicit的,因此不能将一个内置指针隐式转换为一个智能制造,必须直接初始化

shared_ptrp1=new int(2024) //错误,必须使用直接初始化形式 

类似的

shared_ptr clone(int p){ return new int(p); }

也是错误的,可以修改成

shared_ptr clone(int p){ return shared_ptr(new int(p)); }

不要混合使用普通指针和智能指针,使用一个内置指针来访问一个智能指针所负责的对象是很危险的,因为我们无法知道对象何时会被释放

也不要使用get初始化另一个智能指针或者为智能指针赋值。get函数是为了这种情况而设计的:需要向不能使用智能指针的代码传递一个内置指针。使用get返回的指针的代码不能delete该指针。

p.reset() 如果p是唯一指向其对象的shared_ptr,reset会释放此对象。若传递了参数内置指针q,

p.reset(q) 会令p指向q,否则会将p置为空。若还传递了参数d,将会调用d而不是delete来释放q

p.reset(q,d)

使用我们自己的释放 *** 作

不是所有的类都有析构函数,分配了资源,但是没有析构函数来释放这些资源的类,可能会遇到与使用动态内存相同的错误——忘记释放资源。类似的,如果在资源分配和释放之间发送了异常,程序也会发生资源泄露。

void f(信息)
{
    //获得一个连接,使用完后记得释放
    connection c=connect(&d);
    //使用
    //如果f退出前忘记 调用 disconnection,就无法关闭c了
}

同样的,即使记得,如果在关闭前发生异常,也会资源泄露

处理方法,使用我们自己的释放 *** 作
void f(信息)
{
    connection c=connect(&d);
    shared_ptr p(&c,disconnection);
}

当p被销毁时,它不会对自己保存的指针执行delete,而是调用disconnection

当f正常退出,或者异常,p都会被销毁

unique_ptr

一个unique_ptr拥有它指向的对象,某一刻只能有一个unique_ptr指向一个给定的对象。当unique_ptr销毁时,它指向的对象也会被销毁。正因如此,unique_ptr不支持普通的拷贝或赋值 *** 作

unique_ptrp1(new string("hello"));

unique_ptrp2(p1) //错误!不能拷贝
unique_ptrp3;
p3=p2;//错误,不支持赋值

unique_ptr的 *** 作

  • unique_ptru1; 空unique_ptr

  • unique_ptr u2; 空unique_ptr,使用D来释放它的指针

  • unique_ptru(d)

  • u=nullptr; 释放u指向的对象,将u置为空

  • u.release() u放弃对指针的控制权,返回指针,并将u置为空 注意,这里只是切断了联系

  • u.reset() 释放u指向的对象

  • u.reset(q) 如果提供了内置指针q,会将u指向这个对象,否则将u置为空

  • u.reset(nullptr)

关于release

release只是切断了联系,release返回的指针通常用来初始化另一个智能指针,或给智能指针赋值,如果我们不用另一个智能指针保存release返回的指针,那么我们就要手动释放

p2.release();//错误,p2不会释放内存,我们丢失了指针

auto p =p2.release();//正确,后面我们需要delete它
weak_ptr

它指向一个由shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象也会被释放掉。

weak_ptrw 空w,可以指向T类型的对象

weak_ptrw(sp) 与shared_ptr sp指向相同对象的weak_ptr,T必须是能转换为sp指向的类型

w=p

w.reset() 将w置为空

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

w.expired() 若w.use_count为0,返回true,否则返回false

w.lock() 如果expired为true,返回空shared_ptr,否则返回一个指向w的对象的shared_ptr

if(shared_ptrnp = wp.lock()) 
{
    
}

只有wp指向的对象不为空,即lock返回一个shared_ptr时,才会进入条件。安全访问共享对象。

动态数组

不能用begin或end来处理动态数组

释放动态数组,要在其指针前面加上空的方括号对,数组元素将按逆序销毁

如果忽略了,其行为是未定义的。

为了用一个unique_ptr管理动态内存,我们必须在对象类型后面加上一对空括号

unique_ptrup(new int [10]);

当unique_ptr指向一个数组时,我们不能用点和箭头成员运算符,比较其指向的是数组。我们可以使用下标运算符来访问。

注意:unique_ptr指向动态数组时,其release会使用delete[] 自动销毁数组,这和前面的release有点不同

unique_ptrup(new int [42]);
up.release();//up自动用delete[]销毁其指针

注意:与unique_ptr不同,shared_ptr不直接支持管理动态数组,如果希望,必须提供自己定义的删除器

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

在本例中,我们提供了一个lambda,否则默认情况下它的删除是用delete,上面说过对动态数组delete没加方括号,其行为是未定义的。

同时,shared_ptr未定义下标运算符,而且智能指针不支持指针算术运算,因此,为了访问数组中的元素,必须用get获取一个内置指针,如何来访问

for(size_t i =0;i!=10;++i)
    *(sp.get()+i)=i;
allocator类

头文件中

它帮助我们将内存分配和对象构造分离开来,使用例子:

allocatoralloc;	 //可以分配string的allocator对象
auto p =alloc.allocate(n); //分配n个未初始化的string
标准库allocator类及其用法:
  • allocatora 定义了一个名为a的allocator对象

  • a.allocate(n) 分配一段原始的,未构造的内存,保存n个类型为T的对象,返回值应该是首位

  • a.deallocate(p,n) 释放从T*指针p中地址开始的内存;p必须是之前由allocate返回的指针。内存需要提前destroy

  • a.construct(p,args) p必须是一个类型为T*的指针,指向一块原始内存;args被传递给类型为T的构造函 数,用来在p指向的内存中构造对象

  • a.destroy§ p为T*类型的指针

auto q=p  //p是指向首位的
alloc.construct(q++);   //*q为空字符串
alloc.construct(q++,10,'c')//*q为10个c
alloc.construct(q++,"hi")//*q为hi

while(q!=p)
    alloc.destroy(--q);

alloc.deallocate(p,n);
拷贝和填充未初始化内存的算法
  • uninitialized_copy(b,e,b2) 从迭代器b,e之间,拷贝元素到b2开始的原始内存中,b2的内存必须足够

  • uninitialized_copy_n(b,n,b2) 从迭代器b开始拷贝n个元素

  • uninitialized_fill(b,e,t) 在迭代器b,e之间的原始内存内填充t

  • uninitialized_fill_n(b,n,t) 在迭代器b开始的n个内存填充t

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/868090.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存