C++ Primer(第5版) 全书重点学习笔记

目录

第12章 动态内存

12.1 动态内存与智能指针

12.1.6 weak_ptr

12.2 动态数组

12.2.1 new和数组

12.2.2  allocator类


C++ Primer(第5版) 全书重点学习笔记_第1张图片

 

第12章 动态内存

12.1 动态内存与智能指针

12.1.6 weak_ptr

weak_ptr是一种不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象。将一个weak ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。

当我们创建一个weak ptr时,要用一个shared ptr来初始化它:

auto p = make_shared(42);
weak_ptr wp(p);

由于对象可能不存在,我们不能使用weak_ptr直接访问对象,而必须调用lock检查weak_ptr指向的对象是否仍存在。如果存在,lock返回一个指向共享对象的shared ptr。

if (shared_ptr np = wp.lock()) { 
    // 只有当1ock调用返回true时我们才会进入if语句体
}

12.2 动态数组

12.2.1 new和数组

为了让new分配一个对象数组,我们要在类型名之后跟一对方括号,在其中指明要分配的对象的数目。方括号中的大小必须是整型,但不必是常量。

//调用get size确定分配多少个int
int *pia = new int[get size()]; //pia指向第一个int

也可以用一个表示数组类型的类型别名来分配一个数组,这样,new表达式中就不需要方括号了:

typedef int arrT[42];    //arrT表示42个int的数组类型
int *p = new arrT;    //分配一个42个int的数组;p指向第一个int
int *p = new int[42];   //编译器执行这个表达式时还是会用new[]

 typedef用法:【C/C++】中【typedef】用法大全_c++中typedef的用法

当用new分配一个数组时,我们并未得到一个数组类型的对象,而是得到一个数组元素类型的指针。即使我们使用类型别名定义了一个数组类型,new也不会分配一个数组类型的对象。在上例中,new返回的是一个元素类型的指针。
由于分配的内存并不是一个数组类型,因此不能对动态数组调用begin或end。这些函数使用数组维度来返回指向首元素和尾后元素的指针。出于相同的原因,也不能用范围for语句来处理动态数组中的元素。

初始化动态分配对象的数组:默认情况下,new分配的对象,不管是单个分配的还是数组中的,都是默认初始化的。可以对数组中的元素进行值初始化方法是在大小之后跟一对空括号。在新标准中,我们还可以提供一个元素初始化器的花括号列表。

int* pia = new int[10];    //10个未初始化的int
int* pia2 = new int[10]();   //10个值初始化为0的int       
    
int* pia3 = new int[10]{ 0,1,2,3,4,5,6,7,8,9 };   //10个1nt分别用列表中对应的初始化器初始化
string* pia4 = new string[10]{ "a","an","the",string(3,'x') };   //10个string,前4个用给定的初始化器初始化,剩余的进行值初始化

动态分配一个空数组是合法的:当我们用new分配一个大小为0的数组时,new返回一个合法的非空指针,但此指针不能解引用一毕竟它不指向任何元素。

char arr[0];     // 错误:不能定义长度为0的数组
char* cp = new char[0];      //正确:但cp不能解引用

释放动态数组:为了释放动态数组,我们使用一种特殊形式的delete一在指针前加上一个空方括号对。当我们释放一个指向数组的指针时,空方括号对是必需的:它指示编译器此指针指向一个对象数组的第一个元素。如果我们在delete一个指向数组的指针时忽略了方括号其行为是未定义的。

delete cp;    // p必须指向一个动态分配的对象或为空
delete [] cp;    //pa必须指向一个动态分配的数组或为空

智能指针和动态数组:标准库提供了一个可以管理new分配的数组的unique_ptr版本。为了用一个
unique_ptr管理动态数组,我们必须在对象类型后面跟一对空方括号。

当一个unique ptr指向一个数组时,我们不能使用点和箭头成员运算符。毕竟unique ptr指向的是一个数组而不是单个对象,因此这些运算符是无意义的。当一个unique ptr指向一个数组时,我们可以使用下标运算符来访问数组中的元素。

// up指向一个包含10个未初始化int的数组	
unique_ptr up(new int[10]);
for (size_t i = 0; i != 10; ++i) {
	up[i] = i;
	cout << "up" << "[" << i << "]:" << up[i] << endl;
}
//自动用delete[]销毁其指针
up.release();

与unique_ptr不同,shared_ptr不直接支持管理动态数组。如果希望使用shared_ptr管理一个动态数组,必须提供自己定义的删除器。

shared_ptr未定义下标运算符,而且智能指针类型不支持指针算术运算。因此,为了访问数组中的元素,必须用get获取一个内置指针,然后用它来访问数组元素。

 

12.2.2  allocator类

allocator类:标准库allocator类定义在头文件memory中,它帮助我们将内存分配和对象构造分离开来。它提供一种类型感知的内存分配方法,它分配的内存是原始的、未构造的。

为了定义一个allocator对象,我们必须指明这个allocator可以分配的对象类型。当一个allocator对象分配内存时,它会根据给定的对象类型来确定恰当的内存大小和对齐位置。

allocator alloc;    //可以分配string的allocator对象
auto const p = alloc.allocate(8);    //分配n个未初始化的string

allocator分配未构造的内存:allocator分配的内存是未构造的(unconstructed)。我们按需要在此内存中构造对象。在新标准库中,construct成员函数接受一个指针和零个或多个额外参数,在给定
位置构造一个元素。

auto q = p;    // q指向最后构造的元素之后的位置
alloc.construct(q++);    //*q为空字符串
alloc.construct(q++, 10, 'c');    //*q为cccccccccc
alloc.construct(q++, "hi");    //*g为hi!

为了使用allocate返回的内存,我们必须用construct构造对象。使用未构造的内存,其行为是未定义的。

当我们用完对象后,必须对每个构造的元素调用destroy来销毁它们。函数destroy接受一个指针,对指向的对象执行析构函数:

while(q!=p)
    alloc.destroy(--q);  //释放我们真正构造的string

在循环开始处,q指向最后构造的元素之后的位置。我们在调用destroy之前对q进行了递减操作。因此,第一次调用destroy时,q指向最后一个构造的元素。最后一步循环中我们destroy了第一个构造的元素,随后g将与p相等,循环结束。 

释放内存通过调用deallocate来完成

alloc.deallocate(p, n);

拷贝和填充未初始化内存的算法 :标准库还为allocator类定义了两个伴随算法,可以在未初始化内存中创建对象,它们都定义在头文件memory中。

uninitialized_copy(b,e,b2) 从迭代器b和e指出的输入范围中拷贝元素到迭代器b2指定的未构造的原始内存中。b2指向的内存必须足够大,能容纳输入序列中元素的拷贝
uninitialized_copy_n(b,n,b2) 从迭代器b指向的元素开始,拷贝n个元素到b2开始的内存中
uninitialized_fill(b,e,t) 在迭代器b和e指定的原始内存范围中创建对象,对象的值均为t的拷贝
uninitialized_fill_n(b,n,t) 从迭代器b指向的内存地址开始创建n个对象。b必须指向足够大的未构造的原始内存,能够容纳给定数量的对象

你可能感兴趣的:(C++,c++,C++,Primer)