C++ Primer 第12章 知识点回顾

静态内存(局部static对象,类static成员,全局变量)、栈内存(函数内非static变量)、堆(也叫自由空间,存储动态分配的对象,即运行时分配)。动态对象生存期由程序来控制,其他对象由编译器自动创建和销毁。


12.1.1 shared_ptr类

  1. 程序使用动态内存处于一下三种原因之一:
    1. 程序不知道自己需要使用多少对象
    2. 程序不知道所需对象的准确类型
    3. 程序需要在多个对象间共享数据

12.1.2 直接管理内存

  1. string* ps=new string;//默认初始化
  2. string* ps=new string();//值初始化
  3. vector<int> *p=new vector<int>{1,2,3};//列表初始化
  4. 对于内置类型,默认初始化和值初始化差别很大:默认初始化的对象的值是未定义的
  5. auto p1=new auto(obj);
  6. const int* pci=new const int(1024);//一个动态分配的const对象必须进行初始化
  7. 定位new:int *p2=new (nothrow) int;//如果分配失败,new返回一个空指针。 int *p1=new int;//分配失败抛出bad_alloc
  8. delete p(指针);做两件事:销毁给定的指针指向的对象;释放对应的内存
  9. 传递给delete的指针必须指向动态分配的内存,或是一个空指针

12.1.3 shared_ptr和new的结合使用

  1. 接受指针参数的只能指针的构造函数是explicit的,必须使用直接初始化形式来初始化一个只能指针shared_ptr<int> p1(new int(1024));
  2. 我们可以将只能指针绑定到一个指向其他类型资源的指针上,但是为了这样做,必须提供自己的操作来代替delete
  3. 两个智能指针初始化为同一个new 得到的指针,则这两个只能指针是独立的。这么做十分危险

智能指针与异常

  1. 智能指针被销毁时会检查引用计数,若引用计数变为0会对他所管理的指针进行delete操作。我们可以定义自己的删除器代替delete

智能指针使用规范:

  1. 不要使用相同的内置指针初始化或reset多个只能指针(会导致多次free)
  2. 不要delete get()返回的指针
  3. 不适用get()初始化或reset另一个智能指针
  4. 如果你使用只能指针管理的资源不是new分配的内存,记住传递给它一个删除器

12.1.5 unique_ptr

  1. unique_ptr不拥有它指向的对象,因此unique_ptr不支持普通的拷贝和赋值
  2. u.release();//放弃对指针的控制权,返回指针,并将u置为空。但堆内存还在
  3. u.reset(q);//释放u指向的对象(堆内存释放掉了),另u指向q指向的对象
  4. 如果我们不用另一个只能指针来保存release返回的指针,我们的程序就负责资源的释放auto p=p2.release();//正确,但我们必须记得delete(p)
  5. 不能拷贝unique_ptr的例外:我们可以拷贝或赋值一个将要被销毁的unique_ptr
  6. unique_ptr<T,D> u(d);重载一个unique_ptr中的删除器会影响到unique_ptr类型以及如何构造该类型的对象(与关联容器比较类似

12.2.1 new 和数组

  1. int *pia=new int[get_size()];//方括号中的大小必须是整型,但不必是常量
  2. typedef int arrT[42];
    int *p=new arrT;

  3. 由于分配的类型并不是一个数组类型,因此不能对动态数组调用begin和end来返回指向首元素和尾后元素的指针。也不能用范围for语句来处理所谓的动态数组中的元素。要记住我们所说的动态数组并不是数组类型,这是重要的。

  4. int *pia=new int[10];//默认初始化
  5. int *pia=new int[10]();//值初始化
  6. int *pia=new int[3]{1,2};
  7. char *p=new char[0];//动态分配一个空数组是合法的,但p不能解引用。此时new返回一个合法的非空指针(类似于尾后指针、尾后迭代器),可以用于比较。
  8. delete [] p;//销毁p指向数组中的元素,并释放对应的内存,逆序销毁:最后一个最先销毁
  9. unique_ptr<int[]> up(new int[10]);
  10. shared_ptr不直接支持管理动态数组,如果希望其管理动态数组,必须提供自己定义的删除器:shared_ptr<int> sp(new int[10],[](int *p){delete[]p;});
  11. shared_ptr默认使用delete销毁他指向的对象,若不定义自己的删除器,10中会现delete一个动态数组的问题(应该用delete[])

12.2.2 allocator类
1. new灵活上有局限,它将内存分配和对象构造组合在了一起,类似的,delete将对象析构内存释放组合在了一起。
2. allocator类将内存分配和对象构造分离开来。它分配的内存是原始的,未构造的。
3. allocator<string> alloc;
auto const p=alloc.allocate(n);//分配n个未初始化的string

  1. a.construce(p,args);//类似于make_shared函数的参数,这些额外参数必须是与构造的对象的类型相匹配合法的初始化器(args被传递给类型为T的构造函数)
  2. 为了使用allocate返回的内存,我们必须使用construct构造对象。使用未构造的对象是未定义行为。
  3. a.destroy(p);//执行p所指对象的析构函数销毁对象。先销毁每个对象,再释放内存
  4. 我们只能对真正构造了的元素进行destroy操作
  5. 元素被销毁后,可以重新使用这部分内存,也可以将齐归还给系统a.deallocate(p,n);

12.3 使用标准库:文本查询程序

1. 由于QueryResult所需要的数据都保存在一个TextQuery对象中,我们就必须确定如何访问他们。我们可以拷贝行号的set,但这样做可能很耗时。而且,我们当然不希望拷贝vector,因为这可能会引起整个文件的拷贝,而目标只不过是为了打印文件的一小部分而已。

2. 通过返回指向TextQuery对象内部的迭代器(指针),我们可以避免拷贝操作。但是,这种方法开启了一个陷阱:如果TextQuery对象在对应的QueryResult对象之前被销毁,会发生什么?此情况下,后者将引用搞一个不在存在的对象中的数据。

3. 对于QueryResult对象和对应的TextQuery对象的生存期应该同步这一观察结果,其实已经暗示了问题的解决方案。考虑到这两个类概念上共享了数据,可以使用shared_ptr来反映数据结构中的这种共享关系。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值