草庐IT

c++ - 在自定义容器类中使用分配器

coder 2023-06-01 原文

我正在开发一个类似容器的类,并且我想像使用标准容器一样使用标准分配器基础结构。在网上我找到了很多关于如何单独使用 std::allocator 类,或者如何为标准容器定义自定义分配器的 Material ,但是关于如何一般使用符合标准的分配器非常少见,尤其是在 C++11 的上下文中,从编写自定义分配器的角度来看,事情似乎要容易得多,但从容器的角度来看,事情要复杂得多。

所以我的问题是关于如何以最通用的方式正确使用符合标准的分配器,具体来说:

  • 首先,我什么时候应该用这种方式设计自定义容器?使用默认分配器而不是简单的 new/delete 是否存在合理的性能开销(包括缺少优化机会)?
  • 我必须显式调用包含对象的析构函数吗?
  • 如何区分有状态和无状态分配器?
  • 如何处理有状态的分配器?
    • 什么时候(如果有的话)两个实例可以互换(我什么时候可以用一个实例破坏另一个实例分配的内存)?
    • 复制容器的时候也要复制吗?
    • 在容器移动时可以/必须移动它们?
    • 在容器的移动构造函数和移动赋值运算符中,何时可以将指针移动到分配的内存,何时必须分配不同的内存并移动元素?
  • 在这种情况下是否存在异常安全问题?

我对有关 C++11 世界的答案特别感兴趣(它会改变 C++14 中的任何内容吗?)

最佳答案

在下面的所有答案中,我假设您要遵循 C++11 标准定义容器的规则。该标准不要求您以这种方式编写自定义容器。

  • First of all, when should I design a custom container in this way? Is there a sensible performance overhead (including missing optimization opportunities) in using the default allocator instead of plain new/delete?

出于性能原因,自定义分配器最常见和最有效的用途之一是让它在堆栈外分配。如果您的自定义容器不能接受这样的分配器,那么您的客户端将无法执行这样的优化。

  • Do I have to explicitly call contained objects' destructors?

您必须明确调用 allocator_traits<allocator_type>::destroy(alloc, ptr) , 而后者将直接调用 value_type的析构函数,或将调用 destroy allocator_type 的成员.

  • How do I discriminate between stateful and stateless allocators?

我不会打扰。假设分配器是有状态的。

  • How to handle stateful allocators?

非常小心地遵循 C++11 中规定的规则。特别是那些在 [container.requirements.general] 中指定的分配器感知容器。规则太多,这里就不一一列举了。不过,我很乐意回答有关任何这些规则的具体问题。但第一步是获取标准的拷贝,并阅读它,至少是容器要求部分。我推荐the latest C++14 working draft为此目的。

  • When (if ever) are two instances interchangeable (when can I destroy with one instance the memory allocated with another one)?

如果两个分配器比较相等,那么任何一个都可以释放从另一个分配的指针。拷贝(通过复制构造或复制分配)需要比较相等。

  • They have to be copied when the container is copied?

在标准中搜索 propagate_onselect_on_container_copy_construction了解细节。简单的回答是“视情况而定”。

  • They can/have to be moved when the container is moved?

必须用于移动构造。移动分配取决于 propagate_on_container_move_assignment .

  • In the container's move constructor and move assignment operator, when can I move the pointer to allocated memory, and when do I have to allocate different memory and move the elements instead?

新的 move 构造的容器应该通过 move 构造 rhs 的分配器来获得它的分配器。这两个分配器需要比较相等。因此,您可以转移所有已分配内存的内存所有权,您的容器对于该指针的有效状态为 nullptr在右侧。

移动赋值运算符可以说是最复杂的:行为取决于propagate_on_container_move_assignment以及两个分配器是否比较相等。更完整的描述在我的“分配器备忘单”中。

  • Are there issues about exception safety in this context?

是的,吨。 [allocator.requirements] 列出容器可以依赖的分配器要求。这包括哪些操作可以抛出,哪些不能抛出。

您还需要处理分配器的pointer 的可能性。实际上不是 value_type* . [allocator.requirements] 也是寻找这些细节的地方。

祝你好运。这不是一个初学者项目。如果您有更具体的问题,请将它们发布到 SO。要开始,请直接进入标准。我不知道有关该主题的任何其他权威来源。

这是我为自己制作的一份备忘单,其中描述了分配器的行为以及容器的特殊成员。它是用英文写的,不是标准的eze。如果您发现我的备忘单和 C++14 工作草案之间有任何差异,请相信工作草案。一个已知的差异是我添加了 noexcept以标准没有的方式规范。


Allocator behavior:

C() noexcept(is_nothrow_default_constructible<allocator_type>::value);

C(const C& c);

Gets allocator from alloc_traits::select_on_container_copy_construction(c).

C(const C& c, const allocator_type& a);

Gets allocator from a.

C(C&& c)
  noexcept(is_nothrow_move_constructible<allocator_type>::value && ...);

Gets allocator from move(c.get_allocator()), transfers resources.

C(C&& c, const allocator_type& a);

Gets allocator from a. Transfers resources if a == c.get_allocator(). Move constructs from each c[i] if a != c.get_allocator().

C& operator=(const C& c);

If alloc_traits::propagate_on_container_copy_assignment::value is true, copy assigns allocators. In this case, if allocators are not equal prior to assignment, dumps all resources from *this.

C& operator=(C&& c)
  noexcept(
    allocator_type::propagate_on_container_move_assignment::value &&
    is_nothrow_move_assignable<allocator_type>::value);

If alloc_traits::propagate_on_container_move_assignment::value is true, dumps resources, move assigns allocators, and transfers resources from c.

If alloc_traits::propagate_on_container_move_assignment::value is false and get_allocator() == c.get_allocator(), dumps resources, and transfers resources from c.

If alloc_traits::propagate_on_container_move_assignment::value is false and get_allocator() != c.get_allocator(), move assigns each c[i].

void swap(C& c)
  noexcept(!allocator_type::propagate_on_container_swap::value ||
           __is_nothrow_swappable<allocator_type>::value);

If alloc_traits::propagate_on_container_swap::value is true, swaps allocators. Regardless, swaps resources. Undefined behavior if the allocators are unequal and propagate_on_container_swap::value is false.

关于c++ - 在自定义容器类中使用分配器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21221527/

有关c++ - 在自定义容器类中使用分配器的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  8. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  9. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  10. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

随机推荐