第12章 动态内存
程序用堆(heap)来存储动态分配(dynamically allocate)的对象。动态对象的生存期由程序控制。
动态内存与智能指针(Dynamic Memory and Smart Pointers)
C++中的动态内存管理通过一对运算符完成:new
在动态内存中为对象分配空间并返回指向该对象的指针,可以选择对对象进行初始化;delete
接受一个动态对象的指针,销毁该对象并释放与之关联的内存。
新标准库提供了两种智能指针(smart pointer)类型来管理动态对象。智能指针的行为类似常规指针,但它自动释放所指向的对象。这两种智能指针的区别在于管理底层指针的方式:shared_ptr
允许多个指针指向同一个对象;unique_ptr
独占所指向的对象。标准库还定义了一个名为weak_ptr
的伴随类,它是一种弱引用,指向shared_ptr
所管理的对象。这三种类型都定义在头文件memory中。
shared_ptr类(The shared_ptr Class)
智能指针是模板,创建时需要指明指针可以指向的类型。默认初始化的智能指针中保存着一个空指针。
shared_ptr<string> p1; // shared_ptr that can point at a string
shared_ptr<list<int>> p2; // shared_ptr that can point at a list of ints
shared_ptr
和unique_ptr
都支持的操作:
shared_ptr
独有的操作:
make_shared
函数(定义在头文件memory中)在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr
。
// shared_ptr that points to an int with value 42
shared_ptr<int> p3 = make_shared<int>(42);
// p4 points to a string with value 9999999999
shared_ptr<string> p4 = make_shared<string>(10, '9');
// p5 points to an int that is value initialized
shared_ptr<int> p5 = make_shared<int>();
进行拷贝或赋值操作时,每个shared_ptr
会记录有多少个其他shared_ptr
与其指向相同的对象。
auto p = make_shared<int>(42); // object to which p points has one user
auto q(p); // p and q point to the same object
// object to which p and q point has two users
每个shared_ptr
都有一个与之关联的计数器,通常称为引用计数(reference count)。拷贝shared_ptr
时引用计数会递增。例如使用一个shared_ptr
初始化另一个shared_ptr
,或将它作为参数传递给函数以及作为函数的返回值返回。给shared_ptr
赋予新值或shared_ptr
被销毁时引用计数会递减。例如一个局部shared_ptr
离开其作用域。一旦一个shared_ptr
的引用计数变为0,它就会自动释放其所管理的对象。
auto r = make_shared<int>(42); // int to which r points has one user
r = q; // assign to r, making it point to a different address
// increase the use count for the object to which q points
// reduce the use count of the object to which r had pointed
// the object r had pointed to has no users; that object is automatically freed
shared_ptr
的析构函数会递减它所指向对象的引用计数。如果引用计数变为0,shared_ptr
的析构函数会销毁对象并释放空间。
如果将shared_ptr
存放于容器中,而后不再需要全部元素,而只使用其中一部分,应该用erase
删除不再需要的元素。
程序使用动态内存通常出于以下三种原因之一:
- 不确定需要使用多少对象。(容器类)
- 不确定所需对象的准确类型。 (十五章)
- 需要在多个对象间共享数据。(本节例子)
直接管理内存(Managing Memory Directly)
相对于智能指针,使用new
和delete
管理内存很容易出错。
默认情况下,动态分配的对象是默认初始化的。所以内置类型或组合类型的对象的值将是未定义的,而类类型对象将用默认构造函数进行初始化。
string *ps = new string; // initialized to empty string
int *pi = new int; // pi points to an uninitialized int
可以使用值初始化方式、直接初始化方式、传统构造方式(圆括号()
)或新标准下的列表初始化方式(花括号{}
)初始化动态分配的对象。
int *pi = new int(1024); // object to which pi points has value 1024
string *ps = new string(10, '9');