C++学习之allocator类

本文介绍了C++中的allocator类,阐述了其如何分离内存分配与对象构造的过程,通过示例展示了allocator的基本使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文结合该网友的整合的;https://blog.youkuaiyun.com/Xiejingfa/article/details/50955295

在介绍allocator类之前,先介绍一下new和delete的原理:

C++提供了new和delete操作符来管理动态内存空间。new操作通常需要完成两部分工作:

一、是在系统中申请内存空间。

二、是在分配的内存上构造对象。

delete操作也通常需要完成对应的两部分工作:

一、调用相应的析构函数销毁对象。

二、是回收内存。

总结:从这点看,new和delete操作符把内存空间的分配回收与对象的构建销毁紧紧关联在一起。对于new和delete的这种特性,如果我们只是申请单个对象的时候倒是很合适。因为我们几乎可以确定单个对象一定会被使用同时我们也希望将单个对象的内存和初始化组合在一起。

那如果我们需要分配一大块内存呢?这种情形就有点不同。如下图所示:


如果我们new了长度为10的数组,但并没有全部使用呢?这样剩下那些没有使用的元素就这就产生了额外的对象构造的成本。可见,将内存分配和对象构造结合在一起可能会导致不必要的浪费。而且如果类中没有默认的构造函数,我们也无法new出动态数组(这是一种最极端的可能啦)。

有没有一种方法可以将内存的分配回收和对象的构造销毁分离开,让我们在一块内存空间中按需分配对象呢?这就是我们今天要介绍的allocator类。

allocator类是一个模板类,定义在头文件memory中,用于内存的分配、释放、管理,它帮助我们将内存分配和对象构造分离开来。具体地说,allocator类将内存的分配和对象的构造解耦,分别用allocate和construct两个函数完成,同样将内存的释放和对象的析构销毁解耦,分别用deallocate和destroy函数完成。下面我们就来介绍一下这几种函数。

1、内存分配和对象构造

我们可以使用下面语句定义一个名为alloc为类型T分配内存的allocator对象:

allocator<T> alloc;

有了allocator对象后,我们可以使用下面的函数让系统为我们分配一段原始的、未构造的、可以保持n个类型为T的对象的内存空间:

alloc.allocate(n)

与new操作类似,allocate函数调用成功后返回一个指向该段内存第一个元素的指针。allocator分配的对象是未构造的,我们在使用前需要调用construct函数在此内存中构造对象,如果使用为构造的内存,其行为是未定义的。construct的使用方法如下:

alloc.construct(p, args)

p必须是一个类型为T*的指针,指向一块由allocator分配的未构造内存空间。arg为类型T构造函数的参数,用来在p指向的内存空间中构造一个T类型对象。

示例:

#include <iostream>
#include <memory>
using namespace std;

class Example
{
public:
    Example() : a(0) { cout << "example default constructor..." << endl; }
    Example(int x) : a(x) { cout << "example constructor..." << endl; }
    ~Example() { cout << "example destructor..." << endl; }
    int a;
};

int main()
{
    allocator<Example> alloc;
    Example *p = alloc.allocate(2);
    alloc.construct(p);
    alloc.construct(p + 1, 3);
    cout << p->a << endl;
    p++;
    cout << p->a << endl;
}

在上面这段代码中,我们先申请了可以保存两个Example对象的内存空间,然后在第一个位置上调用Example的默认构造函数来构造对象,在第二个位置调用另一个构造函数来构造对象。输出如下:

example default constructor...
example constructor...
0
3

从输出中我们看到,我们只是完成了内存分配和对象构造的工作,构造好的对象并没有析构,内存也没有回收。下面我们来看看如何使用allocator类来完成对象析构和内存回收工作。

2、对象析构和内存回收

当我们使用完对象后,需要对每个构造的对象调用destroy(这是销毁对象,但是内存空间还在)函数来销毁它们。

alloc.destroy(p)

destroy函数接受一个T *的指针,对其指向对象执行构造函数。对象被析构销毁后,分配好的内存空间依然存在,我们可以重新在这块内存上继续构造对象,重复利用,也可以对该内存进行回收操作,归还给系统。内存释放用deallocate函数完成:

alloc.deallocate(p, n)

deallocate函数释放从p开始的长度为n的内存空间,其中p是allocate的返回值,n是该段内存 保存的元素个数,应和allocate的参数n保持一致。需要注意的是,必须由用户来保证调用deallocate前对每个在这块内存中创建的对象调用destroy函数。

示例:

class Example
{
public:
    Example() : a(0) { cout << "example default constructor..." << endl; }
    Example(int x) : a(x) { cout << "example constructor..." << endl; }
    ~Example() { cout << "example destructor..." << endl; }
    int a;
};

int main()
{
    allocator<Example> alloc;
    Example *p = alloc.allocate(2);
    alloc.construct(p);
    alloc.construct(p + 1, 3);
    cout << p->a << endl;
    cout << (p + 1)->a << endl;
    alloc.destroy(p);
    alloc.destroy(p + 1);
    alloc.deallocate(p, 2);

}

输出如下:

example default constructor...
example constructor...
0
3
example destructor...
example destructor...

总结:

  1. allocator类将内存分配回收和对象构造析构分离开来,可以让我们先分配内存再按需构造。
  2. allocator类分配的内存是未构造的,为了使用已经分配好的内存,我们必须使用construct构造对象。如果使用未构造的内存,其行为是未定义的。
  3. 只能对真正构造了的对象进行destroy操作,用户必须保证在调用deallocate函数回收内存前对这块内存上的每个元素调用destroy函数。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值