当前位置:主页 > c/c++教程 > C++ Boost Intrusive

C++ Boost Intrusive库示例精讲

发布:2023-03-08 14:00:01 59


为网友们分享了相关的编程文章,网友殳浩大根据主题投稿了本篇教程内容,涉及到C++、Boost、Intrusive、C++、Boost、Intrusive库、C++ Boost Intrusive相关内容,已被423网友关注,涉猎到的知识点内容可以在下方电子书获得。

C++ Boost Intrusive

一、说明

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 
#include 
#include 
#include 
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_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 
#include 
#include 
#include 
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_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';
}

Example18.2

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

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

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

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

#include 
#include 
#include 
#include 
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_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 
#include 
#include 
#include 
using namespace boost::intrusive;
typedef link_mode mode;
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 constant_time_size constant_time_size;
  typedef list 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 
#include 
#include 
#include 
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_hook> hook;
  typedef set 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 用于哈希容器。

到此这篇关于C++ Boost Intrusive库示例精讲的文章就介绍到这了,更多相关C++ Boost Intrusive内容请搜索码农之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持码农之家!


参考资料

相关文章

  • C++时间函数整理详解

    发布:2023-03-07

    C++中并没有针对时间特意提供特定的时间类型,而是直接继承了C语言的结构以及函数,因此在C++中使用时间函数需要引用<ctime>头文件,这篇文章主要介绍了C++时间函数


  • C++11正则表达式详解(regex_match、regex_search和regex_replace)

    发布:2023-03-02

    正则表达式(regular expression)是计算机科学中的一个概念,又称规则表达式,下面这篇文章主要介绍了C++11正则表达式(regex_match、regex_search和regex_replace)的相关资料,需要的朋友可以参考下


  • C++网络编程详细讲解

    发布:2023-03-08

    计算机是通过TCP/IP协议进行互联从而进行通信的,为了把复杂的TCP/IP协议隐藏起来,更方便的实现计算机中两个程序进行通信,引出了socket这个概念


  • C++实现简易反弹小球游戏的示例代码

    发布:2023-03-03

    我们利用printf 函数实现一个在屏幕上弹跳的小球。弹跳的小球游戏比较简单、容易入门,也是反弹球消砖块、接金币、台球等很多游戏的基础,感兴趣的可以了解一下


  • C++资源管理操作方法详解

    发布:2023-03-02

    系统中的资源,诸如动态申请的内存,文件描述符,数据库连接,网络socket等,在不用的时候,应该及时归还给系统,否则就会造成内存泄露


  • C++ qsort函数排序与冒泡模拟实现流程详解

    发布:2023-03-05

    qsort是一个库函数,基于快速排序算法实现的一个排序的函数,下面这篇文章主要给大家介绍了关于C语言qsort()函数使用的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下


  • Java C++ leetcode面试零矩阵

    发布:2023-03-04

    这篇文章主要为大家介绍了Java C++题解leetcode面试零矩阵示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪


  • C++演讲比赛管理系统实现流程实例

    发布:2023-03-06

    这篇文章主要介绍了C++演讲比赛管理系统实现流程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧


网友讨论