第12章 动态内存

1、动态内存与智能指针

  • 除了自动和static对象外,C++还支持动态分配对象。动态分配的对象的生存期与它们在哪里创建是无关的,只有当显式地被释放时,这些对象才会销毁。
  • 在c++中,动态内存的管理是通过一对运算符来完成的:new,在动态内存中为对象分配空间并返回一个指向该对象的指针;delete,接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。
  • 动态内存的使用很容易出问题,因为确保在正确的时间释放内存是及其困难的。有时候会忘记释放内存,在这种情况下就会产生内存泄露,有时在尚有指针引用内存的情况下就释放了它,在这种情况下就会产生引用非法内存的指针
  • 标准库提供了两种智能指针(smart pointer)类型来管理动态对象。shared_ptr允许多个指针指向同一个对象;unique_ptr则独占所指向对象。标准库还定义了一个名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象。3种类型都定义在memory头文件中。

2、shared_ptr类

  • 智能指针也是模板,创建一个智能指针时,必须提供额外的信息–指针可以指向的类型。
shared_ptr<string> p1;		//指向string
shared_ptr<list<int>> p2;	//指向int的list
  • 智能指针的使用方式与普通指针类似。
  • shared_ptr和unique都支持的操作:
shared_ptr sp 空智能指针,可以指向类型T的对象
unique up
p 将p用作一个条件判断,若p指向一个对象,则为true
*p 解引用p,获得它指向的对象
p->mem 等价于(*p).mem
p.get() 返回p中保存的指针,要小心使用,若智能指针释放了其对象,返回的指针所指向的对象也就消失了
swap(p, q) 交换p和q中的指针
p.swap(q) 交换p和q中的指针
  • shared_ptr独有的操作:
make_shared (args) 返回一个shared_ptr,指向一个动态分配的类型为T的对象,使用args初始化此对象
shared_ptr p(q) p是shared_ptr q的拷贝;此操作会递增q中的计数器,q中的指针必须能转换为T*
p = q p和q都是shared_ptr,所保存的指针必须能相互转换,此操作会递减p的引用计数,递增q的引用计数;若p的引用计数变为0,则将其管理的原内存释放
p.unique() 若p.use_count()为1,返回true;否则返回false
p.use_count() 返回与p共享对象的智能指针数量;可能很慢,主要用于调试

make_shared函数

  • 最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数。此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。
shared_ptr<int> p3 = make_shared<int> (42);
shared_ptr<string> p4 = make_shared<string> (10, '9');

shared_ptr的拷贝和赋值

  • 当进行拷贝或赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象。
  • 每个shared_ptr都有一个关联的计数器,通常称其为引用计数,无论何时我们拷贝一个shared_ptr,计数器都会增加;当我们给shared_ptr赋予一个新值或是shared_ptr被销毁,计数器就会递减。一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象。
  • 程序使用动态内存出于一下三种原因之一:
    1)程序不知道自己需要使用多少对象。
    2)程序不知道所需对象的准确类型。
    3)程序需要在多个对象间共享数据。

3、直接管理内存:new分配内存,delete释放new分配的内存。

  • 在自由空间分配的内存是无名的,因此new无法为其分配的对象命名,而是返回一个指向该对象的指针。
  • delete表达式接受一个指针,指向我们想要释放的对象。
  • 传递给delete的指针必须指向动态分配的内存,或者是一个空指针。释放一块并非new分配的内存,或者将相同的指针值释放多次,其行为是未定义的。

动态内存的管理非常容易出错:使用new、delete管理动态内存存在的三个常见问题

  • 忘记delete内存(内存泄露)。
  • 使用已经释放掉的对象。通过在释放内存后将指针置为空,有时可以检测出这种错误。
  • 同一块内存释放两次。当有两个指针指向相同的动态分配对象时,可能发生这种错误。

delete之后重置指针值

  • 当我们delete一个指针后,指针值就变为无效了,虽然指针已经无效,但在很多机器上指针仍然保存着(已经释放了的)动态内存的地址。在delete之后,指针就变成了空悬指针(dangling pointer),即,指向一块曾经保存数据对象但现在已经无效的内存的指针。
  • 动态内存的一个基本问题是可能有多个指针指向相同的内存。在delete内存之后重置指针的方法只是对这个指针有效,对其他任何仍指向(已释放的)内存的指针是没有作用的。

4、shared_ptr与new结合使用

  • 如果不初始化一个智能指针,它就会被初始化为一个空指针,我们还可以用new返回的指针来初始化智能指针:
shared_ptr<double> p1;	
shared_ptr<int> p2(new int(42));	//p2指向一个值为42的int
  • 接受指针参数的智能指针构造函数是explicit的。因此,不能将一个内置指针隐式转换为一个智能指针,必须使用直接初始化形式。
shared_ptr<int> p1 = new int(1024);		//错误:必须使用直接初始化形式
shared_ptr<int> p2(new int(1023);		//正确:使用了直接初始化形式
  • 定义和改变shared_ptr的其他方法
shared_ptr p(q) p管理内置指针q所指向的对象;q必须指向new分配的内存,且能够转换为T*类型
shared_ptr p(u) p从unique_ptr u那里接管了对象的所有权;将u置为空
shared_ptr p(q,d) p接管了内置指针q所指向的对象的所有权。q必须能转换为T*类型。p将使用可调用对象d来代替delete
shared_ptr p(p2,d) p是shared_ptr p2的拷贝,p将使用可调用对象d来代替delete
p.reset() 若p是唯一指向其对象的shared_ptr,reset会释放此对象。若传递了可选的参数内置指针q,会令p指向q,否则会将p置空。若还传递了参数d,将会调用d而不是delete来释放q
p.reset(q) 若p是唯一指向其对象的shared_ptr,reset会释放此对象,传递了可选的参数内置指针q,会令p指向q
p.reset(q,d) 若p是唯一指向其对象的shared_ptr,reset会释放此对象,传递了参数d,将会调用d而不是delete来释放q
  • 不要混合使用普通指针和智能指针。
  • 不要使用get初始化另一个智能指针或为智能指针赋值。
  • get用来将指针的访问权限传递给代码,你只有在确定代码不会delete指针的情况下,才能使用get。特别是,永远不要get初始化另一个智能指针或者为另一个智能指针赋值。

智能指针陷阱

  • 智能指针可以提供对动态分配的内存安全而又方便的管理,但这建立在正确的使用的前提下。为了正确使用智能指针,我们必须坚持一些基本规范:
    1)不要用相同的内置指针初始化(或reset)多个智能指针。
    2)不delete get()返回的指针。
    3)不适用get()初始化或reset另一个智能指针。
    4)如果你使用get()返回的指针,记住当最后一个对应的智能指针销毁后,你的指针就变为无效了。
    5)如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器。

5、unique_ptr

猜你喜欢

转载自blog.csdn.net/weixin_42205011/article/details/87923213
0条评论
添加一条新回复