c++ primer 笔记第十二章动态内存

第十二章:动态内存

梗概:本章主要讲解使用C++智能指针动态管理内存以及直接管理动态内存的方法以及他们的结合。

静态内存保存static变量以及定义在所有函数之外的变量。栈内存保存函数之内的非static变量,堆内存用来分配给程序动态产生的对象。静态内存和栈内存中的变量由编译器决定其生命周期。静态内存使用前分配,程序结束销毁。栈内存的变量在该程序块执行时存在。动态对象由程序控制声明周期,即需要显示分配和销毁。

12.1 动态内存与智能指针

new在动态内存中为对象分配空间并返回指向该对象的指针,delete接受一个动态对象的指针,销毁对象并释放内存。

忘记使用delete销毁new分配的对象会造成内存泄漏。

新标准提供三种智能指针,智能指针可以自动释放指向的对象。有shared_ptr, unique_ptr和weak_ptr三种。

12.1.1 shared_ptr 类

智能指针是模板,创建需要提供类型。如shared_ptr<string> p1; 默认为空指针。

make_shared<T>(args)函数类似emplace,使用参数构造动态对象。参数为空则进行值初始化。

认为每个shared_ptr都有一个引用计数,有新共享指针指向共享指针指向的对象计数器会加一,销毁减一。计数器为0对象销毁。

shared_ptr调用类的析构函数销毁对象并且释放对象关联的内存。

shared_ptr保证有共享指针指向的对象一直存在,因此需要保证删除不需要的共享指针。特别是在容器中。

使用动态内存的三个原因:一、程序不清楚使用对象数量;二、不清楚对象类型;三、需要在多个对象之间共享数据。

12.1.2 直接管理内存

使用new和delete直接管理动态内存,包含此操作的类不能使用默认的析构函数和赋值拷贝。

new动态分配的对象是默认初始化的,类类型调用默认构造函数。也可以使用直接初始化、列表初始化和值初始化。

类类型的值初始化调用默认构造函数,内置类型值初始化为一个类似0的初值而非未定义。

动态分配const对象必须初始化。

为了防止内存耗尽抛出异常,可以为new输入参数nothrow,叫做定位new。

delete只能释放使用new分配的对象,而且不能重复释放。

内置指针动态分配的对象在显示删除之前一直存在。

为了避免重复释放,delete指针之后把其置为空指针。但是无法保证另外的指针对象不被误用。

12.1.3 shared_ptr 和 new 结合使用

可以使用new返回的指针初始化shared_ptr类型,如shared_ptr<int> p2(new int(42))。

shared_ptr不接受隐式转换,只能使用直接初始化的形式不能使用拷贝初始化。返回shared_ptr类型的函数不能使用返回普通指针。

智能指针默认使用delete销毁对象,因此默认传入可以被delete销毁的对象,否则需要传入自定的销毁函数shared_ptr<T> p(q,d)。

shared_ptr能够和同类型之间协调析构计数,但是和内置指针无法交流。因此用来构造智能指针的内置指针对象无需delete。

也不能将同一块内置指针类型构造不同的共享指针。构造了共享指针之后不应用内置指针继续访问。也不要使用共享指针的get构造另外的共享指针。

reset可以修改共享指针指向的内存。reset会更新引用计数,如果原对象没有其它共享指针引用,会被销毁。

12.1.4 智能指针和异常

智能指针能够在程序抛出异常时自动销毁对象。而内置指针不能自动delete。

可以传递第二个参数作为智能指针指向对象的销毁函数,如shared_ptr<connection> p(&c, end_connection)。

12.1.5 unique_ptr

unique_ptr指向的对象只能被一个unique_ptr指向。必须采用直接初始化形式。

unique_ptr不能赋值和拷贝。可以reset转移指向的对象,reset接受内置指针或空。

release放弃对象,返回指针,将自己置为空。通常用其返回值初始化其它智能指针或者赋值。

可以拷贝或者赋值一个即将被销毁的unique_ptr,如函数返回。

向unique_ptr传递删除函数需要在尖括号中指定类型,如unique_ptr<connection, decltype(end_connection)*> p(&c, end_connection);

12.1.6 weak_ptr

weak_ptr指向一个共享指针指向的对象,不改变引用计数也不控制指针生存期。

使用shared_ptr初始化weak_ptr,可以直接初始化或赋值初始化。

不能用weak_ptr直接访问对象,调用lock函数返回shared_ptr,若对象已销毁返回空共享指针。

12.2 动态数组

直接使用new分配多个对象,不如使用标准库如vector等。

12.2.1 new 和数组

new 类型名后方括号加数目,分配一个对象数组。返回指向第一个对象的指针。

new一个动态数组可以使用值初始化或者一个花括号列表初始化。若列表初始化数量多分配失败,少的部分值初始化。

可以动态分配长度为0的数组,但不能引用。

释放动态数组时使用delete[]。

可以使用unique_ptr智能指针管理动态数组,如unique_ptr<int[]> up(new int[10])。尖括号指定数组类型。

up.release调用delete[]销毁数组。

当unique_ptr指向数组时不能使用点运算符和箭头运算符,可以使用下标运算符访问元素。

shared_ptr默认不支持管理动态数组,除非提供自定义的删除器,一般用lambda函数。

12.2.2 allocator 类

allocator类将内存的分配和对象构造分离。

allocator<T> a; 初始化 a.allocate(n)分配内存; a.deallocate(p,n)收回内存; a.construct(p, args)在指针p处构造一个对象。

a.destroy(p)对p指向的对象执行析构函数,但没有释放内存。

deallocate必须释放的是由allocator分配的内存,而且n的大小相等。

uninitialized_copy(b,e,b2) b2为指向未初始化的内存,b,e是迭代器。uninitialized_copy_n(b,n,b2)类似。返回递增后的b2。

uninitialized_fill(b,e,t)将对象t拷贝到迭代器范围(b,e)中。uninitialized_fill_n(b,n,t)类似。b指向未初始化的内存。

auto p = alloc.allocate(vi.size()*2); 

auto q = uninitialized_copy(vi.begin(), vi.end(), p);

uninitialized_fill_n(q, vi.size(), 42);

猜你喜欢

转载自blog.csdn.net/qq_25037903/article/details/82348405