一、说明

Boost.Intrusive 是一个特别适合在高性能程序中使用的库。该库提供了创建侵入式容器的工具。这些容器替换了标准库中的已知容器。它们的缺点是它们不能像 std::list 或 std::set 那样容易使用。但它们有以下优点:

  • 侵入式容器不会动态分配内存。对 push_back() 的调用不会导致使用 new 进行动态分配。这是侵入式容器可以提高性能的一个原因。
  • 侵入式容器不会动态分配内存。对 push_bacIntrusive 容器的调用存储原始对象,而不是副本。毕竟,它们不会动态分配内存。这带来了另一个优势:诸如 push_back() 之类的成员函数不会抛出异常,因为它们既不分配内存也不复制对象。k() 不会导致使用 new 进行动态分配。这是侵入式容器可以提高性能的一个原因。

这些优势通过更复杂的代码得到了回报,因为必须满足先决条件才能将对象存储在侵入式容器中。您不能将任意类型的对象存储在侵入式容器中。例如,您不能将 std::string 类型的字符串放入侵入式容器中;相反,您必须使用标准库中的容器。

二、示例

示例 18.1 准备了一个类动物,以允许将此类对象存储在侵入式列表中。

示例 18.1。使用 boost::intrusive::list

#include <boost/intrusive/list.hpp>
#include <string>
#include <utility>
#include <iostream>using namespace boost::intrusive;struct animal : public list_base_hook<>
{std::string name;int legs;animal(std::string n, int l) : name{std::move(n)}, legs{l} {}
};int main()
{animal a1{"cat", 4};animal a2{"shark", 0};animal a3{"spider", 8};typedef list<animal> animal_list;animal_list animals;animals.push_back(a1);animals.push_back(a2);animals.push_back(a3);a1.name = "dog";for (const animal &a : animals)std::cout << a.name << '\n';
}

在列表中,总是从另一个元素访问一个元素,通常使用指针。如果侵入式列表要在没有动态内存分配的情况下存储动物类型的对象,则指针必须存在于某处以连接元素。

要将动物类型的对象存储在侵入式列表中,该类必须提供侵入式列表所需的变量以连接元素。 Boost.Intrusive 提供了钩子——从这些类中继承了所需的变量。要允许将动物类型的对象存储在侵入列表中,动物必须从类 boost::intrusive::list_base_hook 派生。

钩子可以忽略实现细节。但是,可以安全地假设 boost::intrusive::list_base_hook 至少提供了两个指针,因为 boost::intrusive::list 是一个双向链表。多亏了基类 boost::intrusive::list_base_hook,animal 定义了这两个指针以允许连接这种类型的对象。

请注意 boost::intrusive::list_base_hook 是一个带有默认模板参数的模板。因此,不需要显式传递任何类型。

Boost.Intrusive 提供类 boost::intrusive::list 来创建一个侵入式列表。此类在 boost/intrusive/list.hpp 中定义,并像 std::list 一样使用。可以使用 push_back() 添加元素,也可以迭代元素。

重要的是要了解侵入式容器不存储副本;他们存储原始对象。示例 18.1 将狗、鲨鱼和蜘蛛写入标准输出,而不是猫。对象 a1 链接到列表中。这就是为什么当程序遍历列表中的元素并显示名称时名称的更改是可见的。

因为侵入式容器不存储副本,所以必须在销毁它们之前从侵入式容器中移除对象。

示例 18.2。删除和销毁动态分配的对象

#include <boost/intrusive/list.hpp>
#include <string>
#include <utility>
#include <iostream>using namespace boost::intrusive;struct animal : public list_base_hook<>
{std::string name;int legs;animal(std::string n, int l) : name{std::move(n)}, legs{l} {}
};int main()
{animal a1{"cat", 4};animal a2{"shark", 0};animal *a3 = new animal{"spider", 8};typedef list<animal> animal_list;animal_list animals;animals.push_back(a1);animals.push_back(a2);animals.push_back(*a3);animals.pop_back();delete a3;for (const animal &a : animals)std::cout << a.name << '\n';
}

Example 18.2

example18.2 使用 new 创建一个动物类型的对象并将其插入到动物列表中。如果您想在不再需要时使用 delete 来销毁该对象,则必须将其从列表中删除。确保在销毁之前从列表中删除该对象——顺序很重要。否则,侵入式容器元素中的指针可能会引用不再包含动物类型对象的内存位置。

因为侵入式容器既不分配也不释放内存,所以当侵入式容器被破坏时,存储在侵入式容器中的对象继续存在。

由于从侵入式容器中删除元素不会自动破坏它们,因此容器提供了非标准扩展。 pop_back_and_dispose() 就是这样的成员函数之一。

示例 18.3。使用 pop_back_and_dispose() 删除和销毁

#include <boost/intrusive/list.hpp>
#include <string>
#include <utility>
#include <iostream>using namespace boost::intrusive;struct animal : public list_base_hook<>
{std::string name;int legs;animal(std::string n, int l) : name{std::move(n)}, legs{l} {}
};int main()
{animal a1{"cat", 4};animal a2{"shark", 0};animal *a3 = new animal{"spider", 8};typedef list<animal> animal_list;animal_list animals;animals.push_back(a1);animals.push_back(a2);animals.push_back(*a3);animals.pop_back_and_dispose([](animal *a){ delete a; });for (const animal &a : animals)std::cout << a.name << '\n';
}

pop_back_and_dispose() 从列表中删除一个元素并销毁它。因为侵入式容器不知道应该如何销毁元素,所以您需要向 pop_back_and_dispose() 传递一个知道如何销毁元素的函数或函数对象。 pop_back_and_dispose() 将从列表中删除对象,然后调用函数或函数对象并将指向要销毁的对象的指针传递给它。示例 18.3 传递了一个调用 delete 的 lambda 函数。

在示例 18.3 中,只有动物中的第三个元素可以使用 pop_back_and_dispose() 删除。列表中的其他元素尚未使用 new 创建,因此不得使用 delete 销毁。

Boost.Intrusive 支持另一种机制来链接元素的删除和销毁。

示例 18.4。使用自动取消链接模式删除和销毁

#include <boost/intrusive/list.hpp>
#include <string>
#include <utility>
#include <iostream>using namespace boost::intrusive;typedef link_mode<auto_unlink> mode;struct animal : public list_base_hook<mode>
{std::string name;int legs;animal(std::string n, int l) : name{std::move(n)}, legs{l} {}
};int main()
{animal a1{"cat", 4};animal a2{"shark", 0};animal *a3 = new animal{"spider", 8};typedef constant_time_size<false> constant_time_size;typedef list<animal, constant_time_size> animal_list;animal_list animals;animals.push_back(a1);animals.push_back(a2);animals.push_back(*a3);delete a3;for (const animal &a : animals)std::cout << a.name << '\n';
}

Hooks 支持一个参数来设置链接模式。链接模式使用类模板 boost::intrusive::link_mode 设置。如果 boost::intrusive::auto_unlink 作为模板参数传递,则选择自动取消链接模式。

自动取消链接模式会在破坏容器时自动从侵入式容器中删除元素。示例 18.4 仅将 cat 和 Shark 写入标准输出。

仅当所有侵入式容器提供的成员函数 size() 没有恒定的复杂性时,才能使用自动取消链接模式。默认情况下,它具有恒定的复杂性,这意味着:size() 返回元素数量所花费的时间不取决于容器中存储了多少元素。打开或关闭恒定复杂性是优化性能的另一种选择。

要更改 size() 的复杂性,请使用类模板 boost::intrusive::constant_time_size,它需要 true 或 false 作为模板参数。 boost::intrusive::constant_time_size 可以作为第二个模板参数传递给侵入式容器,例如 boost::intrusive::list,以设置 size() 的复杂度。

现在我们已经看到侵入式容器支持链接模式,并且可以选择设置 size() 的复杂度,看起来似乎还有很多东西需要发现,但实际上并没有。例如,仅支持三种链接模式,而自动取消链接模式是您唯一需要了解的一种。如果您不选择链接模式,则使用的默认模式足以满足所有其他用例。

此外,没有其他成员函数的选项。除了 boost::intrusive::constant_time_size 之外,没有其他类是您需要了解的。

示例 18.5 引入了使用另一个侵入式容器的挂钩机制:boost::intrusive::set。

示例 18.5。将 boost::intrusive::set 的钩子定义为成员变量

#include <boost/intrusive/set.hpp>
#include <string>
#include <utility>
#include <iostream>using namespace boost::intrusive;struct animal
{std::string name;int legs;set_member_hook<> set_hook;animal(std::string n, int l) : name{std::move(n)}, legs{l} {}bool operator<(const animal &a) const { return legs < a.legs; }
};int main()
{animal a1{"cat", 4};animal a2{"shark", 0};animal a3{"spider", 8};typedef member_hook<animal, set_member_hook<>, &animal::set_hook> hook;typedef set<animal, hook> animal_set;animal_set animals;animals.insert(a1);animals.insert(a2);animals.insert(a3);for (const animal &a : animals)std::cout << a.name << '\n';
}

有两种方法可以将钩子添加到类:从钩子派生类或将钩子定义为成员变量。虽然前面的示例从 boost::intrusive::list_base_hook 派生了一个类,但示例 18.5 使用类 boost::intrusive::set_member_hook 来定义一个成员变量。

请注意,成员变量的名称无关紧要。但是,您使用的钩子类取决于侵入式容器。例如,要将挂钩定义为侵入式列表的成员变量,请使用 boost::intrusive::list_member_hook 而不是 boost::intrusive::set_member_hook。

侵入式容器有不同的钩子,因为它们对元素有不同的要求。但是,您可以使用不同的几个挂钩来允许将对象存储在多个侵入式容器中。 boost::intrusive::any_base_hook 和 boost::intrusive::any_member_hook 让您可以将对象存储在任何侵入式容器中。多亏了这些类,您不需要从多个钩子派生或将多个成员变量定义为钩子。

默认情况下,侵入式容器期望在基类中定义挂钩。如果将成员变量用作挂钩(如示例 18.5),则必须告知侵入式容器使用哪个成员变量。这就是为什么动物和类型挂钩都被传递给 boost::intrusive::set 的原因。 hook 使用 boost::intrusive::member_hook 定义,每当成员变量用作 hook 时都会使用它。 boost::intrusive::member_hook 期望元素类型、钩子类型和指向成员变量的指针作为模板参数。

示例 18.5 按此顺序将鲨鱼、猫和蜘蛛写入标准输出。

除了本章介绍的类 boost::intrusive::list 和 boost::intrusive::set 之外,Boost.Intrusive 还提供了例如单链表的 boost::intrusive::slist 和 boost::intrusive ::unordered_set 用于哈希容器。

【BOOST C++容器专题03】【07】Boost.Intrusive相关推荐

  1. 【BOOST C++容器专题03】【05】Boost.CircularBuffer

    一.提要 库 Boost.CircularBuffer 提供了一个循环缓冲区,它是一个具有以下两个基本属性的容器: 循环缓冲区的容量是恒定的,由您设置.当您调用成员函数(例如 push_back()) ...

  2. Boost:与容器相关的BOOST_TEST_EQ测试

    Boost:与容器相关的BOOST_TEST_EQ测试 实现功能 C++实现代码 实现功能 与容器(boost::container::pmr::vector_of)相关的BOOST_TEST_EQ测 ...

  3. 【BOOST C++字串专题11】Boost.Spirit(三)

    五.rule规则 在 Boost.Spirit 中,解析器由规则组成.由于规则通常基于 Boost.Spirit 提供的解析器,因此没有明显的区别.例如, boost::spirit::ascii:: ...

  4. 【BOOST C++字串专题11】Boost.Spirit(二)

    四.行动 到目前为止,本章中的示例只检测到两件事:解析器是否成功以及解析器在哪里结束.但是,解析器通常以某种方式处理数据,正如您将在下一个示例中看到的那样. Example 11.9. Linking ...

  5. 【BOOST C++字串专题11】Boost.Spirit(一)

    目录 一.Boost.Spirit库介绍 二. boost::spirit::qi::parse()解析格式 三.解析器 一.Boost.Spirit库介绍 本章介绍库 Boost.Spirit. B ...

  6. boost源码剖析之:boost::multi_array

    boost源码剖析之:boost::multi_array 谢轩 刘未鹏 C++的罗浮宫(http://blog.csdn.net/pongba) Note: 并非新作,是以前和老朋友谢轩写的,也可以 ...

  7. Boost学习笔记(一)——Boost使用基础、内存管理

    一.Boost使用基础 Boost库的大部分组件(90%左右),不需要进行编译,直接包含头文件即可使用. #include <boost/logic/tribool.hpp> using ...

  8. C++ Boost库:windows下编译Boost库

    文章目录 1. 需要编译的boost库 2. 编译步骤 3. 库命名规则 4. VS配置库目录 C++ Boost库:简介和第一个示例程序 C++ Boost库:数值转换 lexical_cast C ...

  9. AI公开课:19.03.07王海峰博士《人工智能技术与产业应用》课堂笔记以及个人感悟

    AI公开课:19.03.07王海峰博士<人工智能技术与产业应用>课堂笔记以及个人感悟 演讲内容(部分) 人工智能是新的生产力. 人工智能发展趋势 1.基础理论:小样本.低能耗.可解释. 2 ...

最新文章

  1. datax 高级_GitHub - xhhx55/DataX
  2. 金融风控实战——Hive详解(数据读取、预处理、特征工程)
  3. 信息系统项目管理师-战略管理知识点
  4. opencv 车辆识别_丽水专业人脸自动识别系统网
  5. eclipse和myeclipse中如何关闭自动补全括号,花括号,双引号等功能
  6. ElasticSearch5.x实践_day05_03_Mapping_Meta-Fields
  7. 操作系统概念学习笔记 16 内存管理(二) 段页
  8. pdf.js插件使用记录,在线打开pdf
  9. 图像去模糊代码 python_用Keras搭建GAN:图像去模糊中的应用(附代码)
  10. php运用like乱码,使用MySql和php出现中文乱码的解决方法
  11. C语言libcurl:Uses the CURLINFO_TLS_SESSION data.
  12. Java继承_这10道Java面试题!大部分的人回答不出来
  13. centOS 安装远程桌面
  14. MAC代码下统计代码行数工具
  15. 英超必way体育:曼城6-3曼联,帽子戏法太厉害了
  16. ue4蓝图运行顺序_UE4蓝图编程的第一步
  17. When executing step “qmake“
  18. WannaCry 勒索软件
  19. maya 白天室内灯光_Maya课时:白天的灯光构建视频教程_翼狐网
  20. 文件服务器找不到ad,找不到本地 Exchange 服务器的 AD 对象或者对象无效

热门文章

  1. 为什么买了款正版杀毒软件?
  2. redis-JedisPoolConfig配置
  3. Conflicting collector combinations in option list; please refer to the release notes for the combina
  4. 如何确定两张表的关联关系(一对一,一对多,多对多)
  5. 你可能把A/B测试做错了
  6. xfce4/X11 on Ubuntu18/armhf/ARM/Hisilicon3798
  7. 基于android的二手车交易系统app
  8. 江苏卫视舞蹈演员机器人_小小年纪高超本领令人惊叹?智伴儿童机器人携手江苏卫视打造“了不起的孩子”...
  9. 笔记与心得:《人人都是产品经理 V2.0》--概念:提出与筛选
  10. 【Grafana】介绍