C++中的内存管理(二)

文章目录

某些应用需要一次性为很多对象分配内存的功能。C++中提供了两种一次分配一个对象数组的功能:newallocator,其中后者允许将分配和初始化分离,提供更好的性能和更灵活的内存管理能力。

大多数应用应该使用标准库容器而不是动态分配的数组。使用容器更为简单,更不容易出现内存管理错误并且可能拥有更好的性能。

使用容器的类可以使用默认的拷贝、赋值和析构操作。分配动态数组的类必须定义自己版本的操作,在拷贝、复制以及摧毁对象时管理所管理的内存。

new和数组

cpp 复制代码
//get_size()即使为0,也是合法的。但是不能解引用,类似尾后指针
int *pia = new int[get_size()]; 

typedef int arrT[42];
int *p = new arrT; //分配一个42个int的数组,p指向第一个int

虽然我们通常称new T[]分配的内存为动态数组,但这种叫法有些误导。我们得到的并非要给数组类型的对象,而是得到一个数组元素类型的指针(没有数组的维度)。因此不能对动态数组调用begin和end,也不能使用范围for语句来处理动态数组中的元素。

初始化动态分配对象的数组

默认情况下,new分配的对象都是默认初始化的。可在后面跟一个()进行值初始化:

cpp 复制代码
int *pia = new int[10]; //10个未定义
int *pia2 = new int[10](); //10个0
string *psa = new string[10]; //10个空string
string *psa2 = new string[10](); //10个空string

在新标准中,还可以使用初始化器{}

cpp 复制代码
int *pia3 = new int[10]{0, 1, 2}; //剩余元素进行值初始化
string *psa3 = new string[10]{"a", "an", string(3, 'x')};

释放动态数组

cpp 复制代码
delete p; //p为动态对象或空
delete [] pa; //pa为一个动态分配的数组或空,逆序摧毁数组中元素

智能指针和动态数组

  • unique_ptr
cpp 复制代码
//无需提供自定义删除器,类型为T[]
unique_ptr<int []> up(new int[10]);
up.release(); //自动使用delete []摧毁其指针

//unique_ptr可以使用下标运算符
for(size_t i = 0; i != 10; ++i)
	up[i] = i;
  • shared_ptr
cpp 复制代码
//不直接支持管理动态数组,类型为T,必须提供删除器
shared_ptr<int> sp(new int[10], [](int *p){delete [] p;});

//不支持下表访问
for(size_t i = 0; i != 10; ++i)
	*(sp.get() + i) = i;

allocator

new在灵活性上有一定的局限性。其将内存分配和对象构造组合在了一起。delete同理。当分配一块大内存时,通常计划按需构造对象,因此需要将内存分配和对象构造分离。

cpp 复制代码
string *const p = new string[n];
string s;
string *q = p;
while(cin >> s && q != p + n)
	*q++ = s;
const size_t = q - p;
delete [] p;
  • new表达式分配并初始化了n个string。但是,我们可能并不需要n个string;
  • 对于确实需要使用到的对象,也在初始化后立即赋予了新值。每个使用到的元素都被赋值了两次;
  • 对于没有默认构造函数的类,不能使用动态数组。

allocator类

当一个allocator对象分配内存时,它会根据给定的对象类型来确定恰当的内存大小和对齐位置:

cpp 复制代码
allocator<string> alloc; //可以分配string的对象
auto const p = alloc.allocate(n); //分配n个未初始化的string

初始化

cpp 复制代码
auto p = q;
alloc.construct(q++); //string默认构造函数构建q,q++
alloc.construct(q++, 10, 'c'); //q指向"ccc..."
alloc.construct(q++, "hi"); //q指向"hi"

cout << *p << endl; //ok
cout << *q << endl; //err,当前q还未构造

摧毁对象

当我们使用完对象之后,必须对每个构造的元素调用destroy来摧毁他们。值得注意的是,只有真正构造了的元素才能使用该操作。

cpp 复制代码
while (q != p)
	alloc.destroy(--q);

归还内存

一但元素被摧毁之后,就可以重新使用这部分内存,也可以通过deallocate来归还内存:

cpp 复制代码
alloc.deallocate(p, n);

传递给deallocate的指针不能为空,必须指向alloc分配的内存,n也必须和分配的一致。

拷贝和填充未初始化内存的算法

假定有一个int的vector,希望将其内容拷贝到动态内存中。我们可以分配一块必vector元素所占空间大一倍的动态内存,然后将袁vector的元素拷贝到前一半空间,多一半用一个值填充:

cpp 复制代码
auto p = alloc.allocate(vi.size() * 2);
auto q = uninitialized_copy(vi.begin(), vi.end(), p); //返回最后一个构造元素之后的指针
uninitialized_fill_n(q, vi.size(), 42);
相关推荐
山中月侣5 分钟前
Java多线程编程——基础篇
java·开发语言·经验分享·笔记·学习方法
Davis_121917 分钟前
代码随想录算法训练营27天 | 56. 合并区间、738.单调递增的数字、968.监控二叉树(提高)
数据结构·c++·算法·leetcode·贪心算法
闻缺陷则喜何志丹19 分钟前
【倍增 桶排序】后缀数组
c++·算法·倍增·桶排序·后缀数组·lcp·后缀树
zylyehuo1 小时前
C++核心编程
c++
励志不掉头发的内向程序员1 小时前
STL库——string(类模拟实现)
开发语言·c++
王廷胡_白嫖帝1 小时前
Qt文件压缩工具项目开发教程
java·开发语言·qt
张飞洪2 小时前
C# 13 与 .NET 9 跨平台开发实战:基于.NET 9 与 EF Core 9 的现代网站与服务开发
开发语言·c#·.net
郝学胜-神的一滴2 小时前
使用C++11改进工厂方法模式:支持运行时配置的增强实现
开发语言·c++·程序人生·设计模式
我是场2 小时前
Android14内核调试 - boot & vendor_boot
java·开发语言·spring boot
爱和冰阔落2 小时前
从关机小游戏学 C 语言:分支循环 + 关键字(break/continue)实战
c语言·开发语言