C++ Primer 第12章 动态内存

程序有3中内存分配方式,静态内存用来保存局部static对象,类static数据成员以及定义在任何函数之外的对象,栈内存用来保存定义在函数内的非static对象。静态内存和栈内存中的对象由编译器创建或销毁。程序用堆来存储动态分配的对象,动态对象必须显示销毁。

动态内存与智能指针

用new运算符在动态内存区域为对象分配空间并返回指向该对象的地址,delete运算符接受new返回的指针,销毁指向的对象并释放内存空间。标准库定义了2种智能指针,他们定义在了memory头文件中。

shared_ptr允许多个指针指向同一个对象,智能指针是模板类,因此在创建时需要指定类型。默认初始化的智能指针中保存着一个空指针。

使用make_shared函数分配动态内存并返回指向该对象的shared_ptr,传递给make_shared参数必须与类型的某个构造函数匹配,make_shared调用对应的构造函数初始化对象。

shared_ptr使用引用计数来保证内存的释放,当引用计数变为0时自动释放所管理的内存,当拷贝、赋值一个shared_ptr时会增加引用计数,如赋值时或用一个shared_ptr初始化另一个或给函数传递参数时或作为函数的返回值时。当我们给shared_ptr赋新值时或被销毁时,shared_ptr原来指向的对象的引用计数减1。当引用计数变为0是释放所指向的对象。

使用动态内存的一个常见原因是在多个对象之间共享数据。

当使用shared_ptr类型的数据成员时,可以实现在多个对象中共享同一个数据成员对象。

默认情况下,动态分配的对象是默认初始化的,也可以使用直接初始化方式或传统的构造方式或大括号括起来的初始化列表方式,也可以使用值初始化方式(类类型名后面跟空括号)。可以通过使用auto来推断要分配的对象的类型,如auto p=new auto(obj1);p指向与obj1类型相同的对象,并且该对象用obj1初始化。

可以动态分配const对象,但对象必须进行初始化,如果对象为类类型可以使用默认构造函数初始化。

当使用new分配内存失败时,会抛出std::bad_alloc类型的异常。通过给new传递一个参数可以改变这种默认行为,new(nothrow) int告诉编译器不要抛出异常,若内存分配失败时返回空指针。bad_alloc和nothrow都定义在new头文件中。

通过delete释放由new分配的内存,delete也可以释放一个const类型的对象。注意delete只能释放由new分配的对象。

使用new和delete管理动态内存存在的3个问题,忘记delete内存,使用已经释放掉的内存,同一个对象被释放两次。坚持只使用智能指针就可以避免这些问题。

delete内存后重置指针值,即赋值为nullptr。

可以使用new返回的指针来初始化shared_ptr指针。默认情况下,智能指针只能绑定到使用new分配的动态内存上,也可以绑定到其他类型的指针上,但是需要自定义delete运算符。

不要混合使用智能指针与普通指针,一旦把普通指针绑定到智能指针上就把内存的管理责任交给智能指针。不要混合使用智能指针和普通内置指针。也不要使用get返回的指针初始化另一个智能指针或给内置指针赋值。注意永远不要用get返回的指针初始化另一个智能指针。

通过使用智能指针可以管理需要手动释放资源的对象,只需要在初始化智能指针时传递对应的删除器函数。

不要使用相同的内置指针初始化多个智能指针对象。不要delete get返回的指针。不要使用get返回的指针初始化另一个智能指针。如果使用智能指针管理的资源不是new分配的内存,记得传递给它一个删除器。

unique_ptr拥有它所指向的对象,某一时刻只能有一个unique_ptr指向对象。当unique_ptr被销毁时,它所指向的对象被销毁。

由于unique_ptr拥有它所指向的对象,因此unique_ptr不支持普通的拷贝和赋值操作,unique_ptr创建时必须绑定到它所拥有的对象。但是可以通过调用release或reset把指针的拥有权从一个unique_ptr转移到另一个unique_ptr。

可以拷贝或赋值一个将要销毁的unique_ptr,常见的例子是从函数返回一个unique_ptr对象。

weak_ptr指向一个由shared_ptr管理的对象,不能通过weak_ptr直接访问对象,必须通过lock函数,lock函数返回shared_ptr,通过该shared_ptr访问绑定的对象。

动态数组

尽量使用标准库容器而不是动态数组,通过在类型名后跟方括号来分配对象数组,方括号中是数组元素的个数。分配一个数组会得到一个元素类型的指针,由于分配的内存并不是一个数组类型,因此不能对动态数组调用begin和end,也不能使用范围for语句。记住动态分配的数组并不是数组类型。

分配动态数组时可以通过值初始化来初始化每个元素,方法是在方括号后面跟空的小括号,也可以使用列表初始化器来初始化数组元素。值得注意的是在值初始化的空括号内不能给定初始化器。

可以动态分配一个元素个数为0的数组,new返回的是一个合法的非空指针,但不能对该指针进行解引用,可以像使用尾后迭代器一样使用该指针。

通过delete后面跟方括号来释放动态分配的数组,如果忘记写方括号,程序的行为将是未定义的。

可以通过unique_ptr来管理动态数组,通过在类型参数后面添加方括号使unique_ptr指向数组,可以直接通过下标运算符来访问数组元素但是不能使用点运算符与箭头运算符。无法直接使用shared_ptr直接管理动态数组。

使用new分配内存时把内存分配与对象构造的过程合成在了一起,这限制了程序的灵活性。标准库allocator类定义在头文件memory中,它将内存分配与对象构造分离开来。使用可以分配的对象类型创建allocator对象,通过调用成员函数allocate()来分配内存空间,通过调用construct成员函数来初始化指定位置的对象。

posted @ 2018-12-12 14:38  Jeff-Lee  阅读(230)  评论(0编辑  收藏  举报
2047179505-asdf123456/*-