C++ allocator类使用示例

2023-11-11 20:38
文章标签 c++ 使用 示例 allocator

本文主要是介绍C++ allocator类使用示例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

动态内存管理

之前我们讲述过动态内存的开辟,可以通过new, malloc,以及alloc等方式,本文通过介绍alloc方式,构造一个StrVec类,这个类的功能类似于一个vector,实现字符串的管理,其中包含push一个字符串,动态扩容,析构,回收内存等操作。

StrVec类实现细节

StrVec类实现如下

class StrVec
{
public://无参构造函数StrVec() : elements(nullptr), first_free(nullptr),cap(nullptr) {}//拷贝构造函数StrVec(const StrVec &);//拷贝赋值运算符StrVec &operator=(const StrVec &);//析构函数~StrVec();//拷贝元素void push_back(const std::string &);//返回元素个数size_t size() const { return first_free - elements; }//返回总容量size_t capacity() const { return cap - elements; }//返回首元素地址std::string *begin() const{return elements;}//返回第一个空闲元素地址//也是最后一个有效元素的下一个位置std::string *end() const{return first_free;}private://判断容量不足开辟新空间void chk_n_alloc(){if (size() == capacity())reallocate();}//重新开辟空间void reallocate();// copy指定范围的元素到新的内存中std::pair<std::string *, std::string *> alloc_n_copy(const std::string *, const std::string *);//释放空间void free();//数组首元素的指针std::string *elements;//指向数组第一个空闲元素的指针std::string *first_free;//指向数组尾后位置的指针std::string *cap;//构造string类型allocator静态成员static std::allocator<std::string> alloc;
};

1 elements成员,该成员指向StrVec内部数组空间的第一个元素
2 first_free成员指向第一个空闲元素,也就是有效元素的下一个元素,该元素开辟空间但未构造。
3 cap 指向最后一个元素的下一个位置。
4 alloc为静态成员,主要负责string类型数组的开辟工作。
5 无参构造函数将三个指针初始化为空,并且默认够早了alloc。
6 alloc_n_copy私有函数的功能是将一段区域的数据copy到新的空间,
并且返回新开辟的空间地址以及第一个空闲元素的地址(第一个未构造元素的地址)。
7 chk_n_alloc私有函数检测数组大小是否达到容量,如果达到则调用reallocate重新开辟空间。
8 reallocate重新开辟空间
9 capacity返回总容量
10 size返回元素个数
11 push_back 将元素放入开辟的类似于数组的连续空间中。
12 begin返回首元素地址
13 end返回第一个空闲元素地址,也是最后一个有效元素的下一个位置
无论我们实现push操作还是拷贝构造操作,都要实现realloc,当空间不足时要开辟空间将旧数据移动到新的数据

//重新开辟空间
void StrVec::reallocate()
{string *newdata = nullptr;//数组为空的情况if (elements == nullptr || cap == nullptr || first_free == nullptr){newdata = alloc.allocate(1);// elements和first_free都指向首元素elements = newdata;first_free = newdata;// cap指向数组尾元素的下一个位置。cap = newdata + 1;return;}//不为空则扩充两倍空间newdata = alloc.allocate(size() * 2);//新内存空闲位置auto dest = newdata;//旧内存有效位置auto src = elements;//通过移动操作将旧数据放到新内存中for (size_t i = 0; i != size(); ++i){alloc.construct(dest++, std::move(*src++));}//移动后旧内存数据无效,一定要删除free();//更新数据位置elements = newdata;//更新第一个空闲位置first_free = dest;//更新容量cap = elements + size() * 2;
}

reallocate函数内部判断是否为刚初始化指针却没开辟空间的空数组,如果是则开辟1个大小的空间。
否则则开辟原有空间的两倍,将旧数据移动到新空间,采用了std::move操作,这么做减少拷贝造成的性能开销。
move之后原数据就无效了,所以要调用私有函数free()进行释放。我们实现该free操作

//释放操作
void StrVec::free()
{//判断elements是否为空if (elements == nullptr){return;}auto dest = elements;//要先遍历析构每一个对象for (size_t i = 0; i < size(); i++){// destroy会调用每一个元素的析构函数alloc.destroy(dest++);}//再整体回收内存alloc.deallocate(elements, cap - elements);
}

先通过遍历destroy销毁内存,从而调用string的析构函数,最后在deallocate回收内存。

// copy指定范围的元素到新的内存中,返回新元素的地址和第一个空闲元素地址的pair
std::pair<std::string *, std::string *> StrVec::alloc_n_copy(const std::string *b, const std::string *e)
{auto newdata = alloc.allocate(e - b);//将原数据用来初始化新空间auto first_free = uninitialized_copy(b, e, newdata);return {newdata, first_free};
}

这样利用alloc_n_copy,我们就可以实现拷贝构造和拷贝赋值了

//拷贝构造函数
StrVec::StrVec(const StrVec &strtmp)
{//将形参数据拷贝给自己auto rsp = alloc_n_copy(strtmp.begin(), strtmp.end());//更新elements, cap,first_freeelements = rsp.first;first_free = rsp.second;cap = rsp.second;
}

但是拷贝赋值要注意一点,就是自赋值的情况,所以我们提前判断是否为自赋值,如不是则进行和拷贝构造相同的操作

//拷贝赋值运算符
StrVec &StrVec::operator=(const StrVec &strtmp)
{//防止自赋值if (this == &strtmp){return *this;}//将形参数据拷贝给自己auto rsp = alloc_n_copy(strtmp.begin(), strtmp.end());//更新elements, cap,first_freeelements = rsp.first;first_free = rsp.second;cap = rsp.second;
}

我们可以利用free实现析构函数

//析构
StrVec::~StrVec()
{free();
}

接下来我们实现push_back,将指定字符串添加到数组空间,以及抛出元素

//添加元素
void StrVec::push_back(const std::string &s)
{chk_n_alloc();alloc.construct(first_free++, s);
}//抛出元素
void StrVec::pop_back(std::string &s)
{if (first_free == nullptr){return;}if (size() == 1){s = *elements;alloc.destroy(elements);first_free = nullptr;elements = nullptr;return;}s = *(--first_free);alloc.destroy(first_free);
}

接下来实现测试函数,测试上述操作

void test_strvec()
{auto str1 = StrVec();str1.push_back("hello zack");StrVec str2(str1);str2.push_back("hello rolin");StrVec str3 = str1;string strtmp;str3.pop_back(strtmp);
}

在主函数调用上面test_strvec,运行稳定。

总结

本文通过allocator实现了一个类似于vector的类,管理string变量。演示了拷贝构造,拷贝赋值要注意的事项,同时演示了如何手动开辟内存并管理内存空间。
源码链接
https://gitee.com/secondtonone1/cpplearn
想系统学习更多C++知识,可点击下方链接。
C++基础

这篇关于C++ allocator类使用示例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/392679

相关文章

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

Python常用命令提示符使用方法详解

《Python常用命令提示符使用方法详解》在学习python的过程中,我们需要用到命令提示符(CMD)进行环境的配置,:本文主要介绍Python常用命令提示符使用方法的相关资料,文中通过代码介绍的... 目录一、python环境基础命令【Windows】1、检查Python是否安装2、 查看Python的安

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

OpenCV实现实时颜色检测的示例

《OpenCV实现实时颜色检测的示例》本文主要介绍了OpenCV实现实时颜色检测的示例,通过HSV色彩空间转换和色调范围判断实现红黄绿蓝颜色检测,包含视频捕捉、区域标记、颜色分析等功能,具有一定的参考... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删