标准库中包含一个名为allocator的类,允许我们将分配和初始化分离。使用allocator通常会提供更好的性能和更灵活的内存管理能力。

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

标准库allocator类定义在头文件memory中,它帮助我们将内存分配和对象构造分离开来它提供一种类型感知的内存分配方法,它分配的内存是原始的、未构造的。类似vector,allocator是一个模板。为了定义一个allocator对象,我们必须指明这个allocator可以分配的对象类型。当一个allocator对象分配内存时,它会根据给定的对象类型来确定恰当的内存大小和对齐位置。allocator支持的操作,如下:

allocatro分配的内存是未构造的(unconstructed)。我们按需要在此内存中构造对象。在新标准库中,construct成员函数接受一个指针和零个或多个额外参数,在给定位置构造一个元素。额外参数用来初始化构造的对象。类似make_shared的参数,这些额外参数必须是与构造的对象的类型相匹配的合法的初始化器。

在早期版本的标准库中,construct只接受两个参数:指向创建对象位置的指针和一个元素类型的值。因此,我们只能将一个元素拷贝到未构造空间中,而不能用元素类型的任何其它构造函数来构造一个元素。还未构造对象的情况下就使用原始内存是错误的。为了使用allocator返回的内存,我们必须用construct构造对象。使用未构造的内存,其行为是未定义的。

当我们用完对象后,必须对每个构造的元素调用destroy来销毁它们。函数destroy接受一个指针,对执行的对象执行析构函数。我们只能对真正构造了的元素进行destroy操作。一旦元素被销毁后,就可以重新使用这部分内存来保存其它string,也可以将其归还给系统。释放内存通过调用deallocate来完成。我们传递给deallocate的指针不能为空,它必须指向由allocate分配的内存。而且,传递给deallocate的大小参数必须与调用allocate分配内存时提供的大小参数具有一样的值。

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

在C++中,内存是通过new表达式分配,通过delete表达式释放的。标准库还定义了一个allocator类来分配动态内存块。分配动态内存的程序应负责释放它所分配的内存。内存的正确释放是非常容易出错的地方:要么内存永远不会被释放,要么在仍有指针引用它时就被释放了。新的标准库定义了智能指针类型------shared_ptr、unique_ptr和weak_ptr,可令动态内存管理更为安全。对于一块内存,当没有任何用户使用它时,智能指针会自动释放它。现代C++程序应尽可能使用智能指针。

std::allocator是标准库容器的默认内存分配器。你可以替换自己的分配器,这允许你控制标准容器分配内存的方式。

以上内容主要摘自:《C++Primer(Fifth Edition 中文版)》第12.2.2章节

下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:

#include "allocator.hpp"
#include <iostream>
#include <memory>
#include <string>
#include <vector>namespace allocator_ {// reference: C++ Primer(Fifth Edition) 12.2.2
int test_allocator_1()
{std::allocator<std::string> alloc; // 可以分配string的allocator对象int n{ 5 };auto const p = alloc.allocate(n); // 分配n个未初始化的stringauto q = p; // q指向最后构造的元素之后的位置alloc.construct(q++); // *q为空字符串alloc.construct(q++, 10, 'c'); // *q为ccccccccccalloc.construct(q++, "hi"); // *q为histd::cout << *p << std::endl; // 正确:使用string的输出运算符//std::cout << *q << std::endl; // 灾难:q指向未构造的内存std::cout << p[0] << std::endl;std::cout << p[1] << std::endl;std::cout << p[2] << std::endl;while (q != p) {alloc.destroy(--q); // 释放我们真正构造的string}alloc.deallocate(p, n);return 0;
}int test_allocator_2()
{std::vector<int> vi{ 1, 2, 3, 4, 5 };// 分配比vi中元素所占用空间大一倍的动态内存std::allocator<int> alloc;auto p = alloc.allocate(vi.size() * 2);// 通过拷贝vi中的元素来构造从p开始的元素/* 类似拷贝算法,uninitialized_copy接受三个迭代器参数。前两个表示输入序列,第三个表示这些元素将要拷贝到的目的空间。传递给uninitialized_copy的目的位置迭代器必须指向未构造的内存。与copy不同,uninitialized_copy在给定目的位置构造元素。类似copy,uninitialized_copy返回(递增后的)目的位置迭代器。因此,一次uninitialized_copy调用会返回一个指针,指向最后一个构造的元素之后的位置。*/auto q = std::uninitialized_copy(vi.begin(), vi.end(), p);// 将剩余元素初始化为42std::uninitialized_fill_n(q, vi.size(), 42);return 0;
}// reference: http://www.modernescpp.com/index.php/memory-management-with-std-allocator
int test_allocator_3()
{std::cout << std::endl;std::allocator<int> intAlloc;std::cout << "intAlloc.max_size(): " << intAlloc.max_size() << std::endl;int* intArray = intAlloc.allocate(100);std::cout << "intArray[4]: " << intArray[4] << std::endl;intArray[4] = 2011;std::cout << "intArray[4]: " << intArray[4] << std::endl;intAlloc.deallocate(intArray, 100);std::cout << std::endl;std::allocator<double> doubleAlloc;std::cout << "doubleAlloc.max_size(): " << doubleAlloc.max_size() << std::endl;std::cout << std::endl;std::allocator<std::string> stringAlloc;std::cout << "stringAlloc.max_size(): " << stringAlloc.max_size() << std::endl;std::string* myString = stringAlloc.allocate(3);stringAlloc.construct(myString, "Hello");stringAlloc.construct(myString + 1, "World");stringAlloc.construct(myString + 2, "!");std::cout << myString[0] << " " << myString[1] << " " << myString[2] << std::endl;stringAlloc.destroy(myString);stringAlloc.destroy(myString + 1);stringAlloc.destroy(myString + 2);stringAlloc.deallocate(myString, 3);std::cout << std::endl;return 0;
}//
// reference: http://en.cppreference.com/w/cpp/memory/allocator
int test_allocator_4()
{std::allocator<int> a1;   // default allocator for intsint* a = a1.allocate(1);  // space for one inta1.construct(a, 7);       // construct the intstd::cout << a[0] << '\n';a1.deallocate(a, 1);      // deallocate space for one int// default allocator for stringsstd::allocator<std::string> a2;// same, but obtained by rebinding from the type of a1decltype(a1)::rebind<std::string>::other a2_1;// same, but obtained by rebinding from the type of a1 via allocator_traitsstd::allocator_traits<decltype(a1)>::rebind_alloc<std::string> a2_2;std::string* s = a2.allocate(2); // space for 2 stringsa2.construct(s, "foo");a2.construct(s + 1, "bar");std::cout << s[0] << ' ' << s[1] << '\n';a2.destroy(s);a2.destroy(s + 1);a2.deallocate(s, 2);return 0;
}} // namespace allocator_

GitHub:  https://github.com/fengbingchun/Messy_Test

C++中std::allocator的使用相关推荐

  1. C++/C++11中std::string用法汇总

    C++/C++11中std::string是个模板类,它是一个标准库.使用string类型必须首先包含<string>头文件.作为标准库的一部分,string定义在命名空间std中. st ...

  2. c++11 动态内存管理-分配器 (std::allocator)

    定义于头文件 <memory> template< class T >struct allocator; (1) template<>struct allocato ...

  3. std::allocator

    1.什么是std::allocator. std::allocator是C++中将内存分配与构造初始化分离开的类.在类中封装了方法,使得操作内存变得更容易.头文件是<memory> 2.a ...

  4. C++11中std::promise的使用

    C++11中的std::promise是个模板类.一个std::promise对象可以存储由future对象(可能在另一个线程中)检索的T类型的值或派生自std::exception的异常,并提供一个 ...

  5. 利用std::allocator实现自定义的vector类

    std::allocator即空间配置器,用于内存分配.更多的细节建议大家研究相关源码. 这里仅是利用std::allocator来实现简单的自定义vector类,如有问题欢迎指正. 1 #inclu ...

  6. c++中std::find_std :: find()与C ++中的示例

    c++中std::find find()作为STL函数 (find() as a STL function) find() is an STL function that comes under th ...

  7. C++中std::endl的作用

    在进行c++开发中std::cout末尾常常会跟一个std::endl,以前理解是换行,但是"\n"也是换行,今天发现除了换行的作用还有就是强制刷新缓冲区 std::endl作用 ...

  8. C++中std::string::find_last_of用法

    早上本来还看代码时本来还迷迷糊糊,结果被find_last_of一下给震醒了,std::string 使用了这么多年,今天才发现find_last_of只是用来查找字符的,多年来一直以为它可用来查找字 ...

  9. C++11中std::packaged_task的使用

    C++11中的std::packaged_task是个模板类.std::packaged_task包装任何可调用目标(函数.lambda表达式.bind表达式.函数对象)以便它可以被异步调用.它的返回 ...

最新文章

  1. ‘wmic‘ 不是内部或外部命令,也不是可运行的程序 解决方法
  2. More about Tair (NoSql)
  3. android动画回到原位,使用动画隐藏view后,原来的位置空白
  4. AI领域内,敢和BAT决斗的创业公司都在这了!
  5. POJ1486模拟或者匈牙利变种
  6. mysql select time,MySql查询时间段的方法
  7. 结合Boost.Geometry和Boost.Assign以及Boost.Range和Boost.Tuple的例子
  8. QT绘制具有向下钻取效果的园饼图
  9. 在某游戏公司面试游戏运营的感受
  10. XSD(Schema)教程
  11. PHP页面显示中文字符出现乱码
  12. DHCP服务器控制企业成本
  13. 华为鸿蒙系统多而能使用吗,【图片】华为鸿蒙系统的厉害之处在于 你可能非用不可 !【手机吧】_百度贴吧...
  14. 华中科技大学计算机作业试题,华中科技大学2009大学计算机基础考试试题A
  15. 标准盒模型与怪异盒模型的区别
  16. java 埋点_数据采集之js埋点
  17. java jstat m ccs_jstat命令详解
  18. 视频剪辑软件,AI智能合并随机生成新视频,并设置视频数量以及时长
  19. 使用FME 批量OSGB转FBX(OBJ) (亲测)
  20. Linux Namespace 入门系列:Namespace API

热门文章

  1. 高效会议平台MAXHUB在京发布,开启会议平台化时代
  2. 基于FPGA的双目相机目标深度图像提取实现——详细版
  3. SmartTalk Solar车载太阳能扬声器 让你与Siri畅谈人生
  4. 这家公司用AI算法开除了150名员工!网友:属实是人工智能,能治工人...
  5. 有名管道FIFO,进行双进程的“双向通信”
  6. 【Selenium】2019 chromedriver的官网版本对应关系
  7. Unity中作2D藤曼生长效果
  8. htmlcss小白笔记
  9. 因时电动夹爪使用代码
  10. SQL Server Management Studio 访问远程数据库