53、delete p、delete [] p、allocator都有什么作用?

发布时间 2023-07-03 21:51:27作者: CodeMagicianT

53、delete p、delete [] p、allocator都有什么作用?

1、 动态数组管理new一个数组时,[]中必须是一个整数,但是不一定是常量整数,普通数组必须是一个常量整数;

delete p是用于释放由new运算符分配的单个对象的内存。如果使用new[]运算符创建了一个对象数组,那么应该使用delete[] p来释放内存,而不是delete p

delete[] p是用于释放由new[]运算符分配的对象数组的内存。在释放内存时,delete[] p会调用每个对象的析构函数,然后释放整个数组的内存。

需要注意的是,如果使用new[]创建了一个对象数组,但使用delete[] p释放了单个对象的内存,这可能会导致未定义的行为。同样地,如果使用new创建了一个单个对象,但使用delete[] p释放了内存,这也可能会导致未定义的行为。

因此,在使用deletedelete[]时,应该确保正确地匹配使用。如果存在任何疑问,建议查看代码以确保正确释放内存。

2、 new动态数组返回的并不是数组类型,而是一个元素类型的指针;

3、 delete[]时,数组中的元素按逆序的顺序进行销毁;

4、 new在内存分配上面有一些局限性,new的机制是将内存分配和对象构造组合在一起,同样的,delete也是将对象析构和内存释放组合在一起的。allocator将这两部分分开进行,allocator申请一部分内存,不进行初始化对象,只有当需要的时候才进行初始化操作。

在C++中,allocator(分配器)是用于管理内存分配和释放的模板类。它是STL(标准模板库)中的一个重要组成部分,用于实现容器类(如vector、list、map等)的内存管理。

allocator的主要作用如下:

1.内存分配:allocator负责为容器中的元素分配内存。它使用动态内存分配机制(如new和malloc)从堆上分配内存,并返回指向分配内存的指针。

2.内存释放:当元素被从容器中移除或容器被销毁时,allocator负责释放先前分配的内存。它使用delete和free等函数释放先前分配的内存。

3.对象构造和析构:allocator还负责在分配的内存空间中构造和析构对象。当新元素被添加到容器中时,allocator使用元素的构造函数来创建对象,并在元素被移除时调用析构函数来销毁对象。

4.内存对齐:allocator还负责按照特定的内存对齐方式来分配内存,以确保对象的起始地址满足对齐要求。这对于某些类型的对象(如带有对齐要求的结构体)非常重要。

通过使用allocator,STL容器可以管理内存分配和释放,从而避免了手动管理内存的繁琐工作,并提供了更高级别的抽象,使开发人员能够更专注于业务逻辑的实现。

allocator类

new有些灵活性上的局限,其中一方面表现在它将内存分配和对象构造组合在了一起。类似的,delete将对象析构和内存释放组合在了一起。我们分配单个对象时,通常希望将内存分配和对象初始化组合在一起。因为在这种情况下,我们几乎肯定知道对象应有什么值。
当分配一大块内存时,我们通常计划在这块内存上按需构造对象。在此情况下,我们希望将内存分配和对象构造分离。这意味着我们可以分配大块内存,但只在真正需要时才真正执行对象创建操作(同时付出一定开销)。

表 12.7: 标准库allocator类及其算法
allocator<T> a 定义了一个名为a的allocator 对象,它可以为类型为T的对象分配内存
a.allocate(n) 分配一段原始的、未构造的内存,保存n个类型为T的对象
a.deallocate(p, n) 释放从T*指针p中地址开始的内存,这块内存保存了n个类型为T的对象;p必须是一个先前由allocate返回的指针,且n必须是p创建时所要求的大小。在调用deallocate之前,用户必须对每个在这块内存中创建的对象调用destroy
a.construct(p, args) p必须是一个类型为T *的指针,指向一块原始内存;arg被传递给类型为T的构造函数,用来在p指向的内存中构造一个对象
a. destroy (p) p为T * 类型的指针,此算法对p指向的对象执行析构函数

一般情况下,将内存分配和对象构造组合在一起可能会导致不必要的浪费。例如:

string *const p = new string[n]; //构造n个空string
string s; 
string *q = p; 
while (cin >> s && q !=p + n) 
    *q++ = s; //赋子*q一个新值
const size_t size = q -p; //记住我们读取了多少个string
//使用数组
delete[] p; // p指向一个数组;记得用delete[]来释放

new表达式分配并初始化了n个string。但是,我们可能不需要n个string,少量­string可能就足够了。这样,我们就可能创建了一些永远也用不到的对象。而且,对于那些确实要使用的对象,我们也在初始化之后立即赋予了它们新值。每个使用到的元素都被赋值了两次:第一次是在默认初始化时,随后是在赋值时。

更重要的是,那些没有默认构造函数的类就不能动态分配数组了。

allocator类

标准库allocator类定义在头文件memory中,它帮助我们将内存分配和对象构造分离开来。它提供种类型感知的内存分配方法,它分配的内存是原始的、未构造的。表 12.7概述了allocator支持的操作。在本节中,我们将介绍这些allocator操作。

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

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

这个allocate调用为n个string分配了内存。

表 12.7: 标准库allocator类及其算法
allocator<T> a 定义了一个名为a的allocator 对象,它可以为类型为T的对象分配内存
a.allocate(n) 分配一段原始的、未构造的内存,保存n个类型为T的对象
a.deallocate(p, n) 释放从T*指针p中地址开始的内存,这块内存保存了n个类型为T的对象;p必须是一个先前由allocate返回的指针,且n必须是p创建时所要求的大小。在调用deallocate之前,用户必须对每个在这块内存中创建的对象调用destroy
a.construct(p, args) p必须是一个类型为T *的指针,指向一块原始内存;arg被传递给类型为T的构造函数,用来在p指向的内存中构造一个对象
a. destroy (p) p为T * 类型的指针,此算法对p指向的对象执行析构函数

allocator分配未构造的内存