“C++ Primer”


关联容器

标准库提供 8 个关联容器,他们的不同体现在:

  1. 或者是一个set,或者是一个map
  2. 或者要求不重复的关键字,或者允许重复关键字,允许重复关键字的容器的名字中都包含单词multi
  3. 按顺序保存或者无序保存,不保持关键字按顺序存储的容器的名字都以unordered开头

pair

定义在头文件utility中,pair是一个用来生成特定类型的模板,当创建一个pair时,必须提供两个类型名


### 动态内存

##### 静态内存

静态内存用来保存局部```static```对象、类```static```数据成员以及定义在任何函数外的变量,由编译器自动分配和销毁,```static```对象在使用之前分配, 在程序结束时销毁

##### 栈内存

用来保存定义在函数内的非```static```对象,由编译器自动分配和销毁,仅在其定义的程序块运行时才存在

#### 智能指针

与常规指针的区别:负责自动释放所指向的对象

###### ```shared_ptr```

1. ```make_shared()```函数:最安全的分配和使用动态内存的方法

```c++
shared_ptr<int> p3 = make_shared<int>(42);	// 指向一个值为 42 的 int 的 shared_ptr
  1. shared_ptr的拷贝和赋值

    每个shared_ptr都有一个关联的计数器,称为引用计数,无论何时我们拷贝一个shared_ptr,计数器都会递增,如用一个shared_ptr初始化另一个shared_ptr,或将它作为参数传递给一个函数以及作为函数的返回值;当给shared_ptr赋一个新值或是shared_ptr被销毁,计数器递减

    一旦一个shared_ptr的计数器变为 0 ,它就会自动释放自己管理的对象

    shared_ptr的析构函数会递减它所指的对象的引用计数,如果引用计数变为0,shared_ptr的析构函数就会销毁对象,并释放内存

直接管理内存

new

默认情况下,动态分配的对象是默认初始化的,内置类型或组合类型的对象的值是未定义的,类类型对象将使用默认构造函数进行初始化

int *pi = new int;	// pi 指向一个动态分配的,为初始化的无名对象
int *pi2 = new int();	// 值初始化为 0,*pi2 为0
string *ps = new string;	// 初始化为空的 string

值初始化的内置类型对象有着良好定义的值,而默认初始化的对象的值则是未定义的

空悬指针

指向一块曾经保存数据对象但现在已经无效的内存的指针

智能指针陷阱
  1. 不使用相同的内置指针值初始化(或 reset)多个智能指针
  2. delete get()返回的指针
  3. 不使用get()初始化或reset另一个智能指针
  4. 如果使用get()返回的指针,记住当最后一个对应的智能指针销毁后,你的指针就变为无效了
  5. 如果使用智能指针管理的资源不是new分配的内存,记住给它传递一个删除器

unique_ptr

一个unique_ptr拥有它所指向的对象,与shared_ptr不同,某个时刻只能有一个shared_ptr指向一个给定对象,也没有make_shared这样的标准库函数,当定义一个unique_ptr时,需要将其绑定到一个new返回的指针上,初始化unique_ptr必须采用直接初始化形式

unique_ptr<double> pl;
unique_ptr<int> p2(new int(42));
unique_ptr<string> p3(new string("test"));
unique_ptr<string> p4(p3);	// 错误,unique_ptr 不支持拷贝
unique_ptr<string> p5;	
p5 = p3;	// 错误,unique_ptr 不支持赋值

unique_ptr<string> p6(p3.release());	// 将所有权从 p3 转移给 p6, release 将 p3 置为空
unique_ptr<string> p7(new string("test"));
p4.reset(p7.release());	// 将所有权从 p7 转移给 p4, release 将 p3 置为空,并返回指针,p4 释放原来指向的内存

```c++
p2.release();	// 错误, p2 不会释放内存,而且我们丢失了指针
auto p = p2.release();	// 正确,但必须记得 delete(p)

不能拷贝unique_ptr规则例外:我们可以拷贝或赋值一个将要被销毁的unique_ptr

unique_ptr<int> clone(int p) {
  return unique_ptr<int>(new int (p));
}

weak_ptr


由于对象可能不存在,不能直接使用```weak_ptr```访问对象,必须调用```lock```

#### lock

此函数检查```weak_ptr```指向的对象是否存在,如果存在,返回一个指向共享对象的```shared_ptr```

#### 动态数组

1. ```new```和数组

   ```c++
   int *pia = new int[get_size()];	// 方括号中必须是整型但不必是常量

   ##### 智能指针与动态数组

   标准库提供了一个可以管理```new```分配的数组的```unique_ptr```版本,使用时需要在对象类型后加上一对空方括号

   ```c++
   unique_ptr<int[]> up(new int[10]);	// up 指向一个包含 10 个为初始化 int 的数组
  1. 
    ```new```将内存分配和对象构造组合在了一起,标准库```allocator```类定义在头文件```memory```中,它帮助我们将在内存分配和对象构造分离开来,它分配的内存是原始的、未构造的
    
    ```c++
    allocator<string> alloc;	// 可以分配 string 的 allocator 对象
    auto const p = alloc.allocate(n);	// 分配 n 个未初始化的 string
    auto q = p;	// q 指向最后构造的元素之后的位置
    alloc.construct(q++);	// *q 为空字符串
    alloc.construct(q++, 10, 'c');	// *q 为 cccccccccc
       
    while(q != p)
      alloc.destroy(--q);	// 释放构造的 string
       
    alloc.deallocate(p, n);	// 释放内存