简述
静态内存用来保存局部 static 对象, 类 static 数据成员, 和函数之外的变量, 而栈内存用来保存函数内的非 static 对象. 这两部分内存都是经由编译器自动创建和销毁的, 而除此之外还有一个内存池, 一般被称为堆, 是用来存储动态分配的对象. 这些对象需要交由程序自己控制生存期.
直接分配
malloc 和 free 是 C 语言的库函数, 在栈中动态分配内存, 而在 C++ 里面, 定义了两个运算符 new/delete 来分配和释放内存. 它们可以通过被重载而实现不同于默认分配的新的分配方式,
分配 new: 分配一个值, 直接使用 new + 类型即可, 返回的是该类型的指针. 而如果分配数组, 则是 new + 类型 [数组大小], 返回的是指向第一个元素的指针.
- int* pi = new int; // 直接分配未初始化的对象指针
- const int* p = new const int(1024); // 使用直接初始化方式分配
- int* pArray = new int[10]; // 分配数组
释放 delete: 在动态内存使用过后, 需要释放空间, 此时需要通过 delete 来释放. 如果释放一个对象, 使用 delete + 对象名称. 如果释放一个数组, 则需要 delete[] + 数组名称, 一定要记得 [], 否则会造成内存泄漏. 同时, 一个空间被释放后, 指针指向的区域就是无效的了, 所以在 delete 之后还需要将原来的指针设为 nullptr, 否则将会产生空悬指针 (dangling pointer).(当然即使是这样也并不是就完全安全了, 如果还有另外的指针指向同一个地址, 那么如果不把这个指针设为 nullptr, 依然会产生问题)
- delete pi;
- delete p;
- delete[] pArray;
- pi = nullptr;
- p = nullptr;
- pArray = nullptr;
智能指针
因为直接分配动态内存十分容易引发问题, 所以在 c++11 标准中为了更容易和更安全的使用动态内存, 提供了几种智能指针来方便我们管理动态对象. 智能指针与指针的行为很类似, 而最重要的区别就在于自动释放对象. 智能指针也是一个模板类, 在创建的时候同样需要提供指针指向的类型.
shared_ptr
shared_ptr 允许多个指针指向同一个对象, 使用方式与普通指针类似, 可以认为 shared_ptr 有一个引用计数, 通过该计数来自动决定何时销毁.
- shared_ptr<string> p1;
- if (p1 && p1->empty()) *p1 = "hi"; //* 操作,-> 操作, 指针判断都可以使用
- shared_ptr<int> p2(new int(33)); // 通过直接初始化形式初始化指针
更安全使用 shared_ptr 来进行动态内存分配的方式是调用 make_shared 函数. make_shared 函数在动态内存中分配一个对象并初始化, 返回指向该对象的 shared_ptr:
- shared_ptr<int> p2 = make_shared<int>(22);
- auto p3 = make_shared<string>("hello");
每当进行一次赋值或者拷贝操作时, shared_ptr 都会递增自己的计数器, 而每当自身离开某个作用域或者被重新赋值而应被清除时, 递减自己的计数器. 当销毁计数为最后一个时, shared_ptr 就会自动调用析构函数销毁自身, 同时释放内存. 正是通过这个计数器, shared_ptr 实现了智能指针的更安全使用动态内存的方法.
unique_ptr
unique_ptr 与 shared_ptr 不同, 同一时间只能有一个 unique_ptr 指向一个给出的对象, 当其被销毁时, 指向的对象也被销毁. 定义 unique_ptr 时, 需要绑定到一个 new 返回的指针上, 所以初始化 unique_ptr 必须采用直接初始化形式, 而且因为只能有一个 unique_ptr 指向给定的对象, 所以不支持拷贝和赋值操作.
unique_ptr<int> p(new int(10));
虽然不能拷贝和赋值, 但可以通过 release 或 reset 将所有权转移, release 操作返回保存的指针并清空自己:
- unique_ptr<string> p2(p1.release());
- unique_ptr<string> p3(new string("test"));
- p2.reset(p3.release());
对于动态数组, 也可以使用 unique_ptr 来管理:
- unique_ptr<int[]> up(new int[10]);
- up.release(); //release 操作也会自动调用 delete[] 来销毁指向的数组
- weak_ptr
weak_ptr 指向由 shared_ptr 指向的对象, 但其不控制对象的生命周期, 绑定 weak_ptr 到 shared_ptr 不会引起引用计数的改变. 而如果 shared_ptr 指向的最后一个对象被销毁引起 shared_ptr 被销毁, 对象就会被释放, 而不会理会是否还有 weak_ptr 指向该对象, 所以 weak_ptr 就是一种弱共享对象.
- shared_ptr<int> p = make_shared<int>(10);
- weak_ptr<int> p2(p);
因为 weak_ptr 指向的对象可能已经不存在, 所以不能直接访问对象, 而需要通过调用 lock 之后, 返回的 shared_ptr 来访问.
- if (shared_ptr<int> np = p2.lock())
- {
- //weak_ptr 指向的对象存在, 并通过 np 与对象共享
- }
来源: http://www.bubuko.com/infodetail-3335330.html