侵入式容器和非侵入式容器之间的主要区别在于,在 C++ 中,非侵入式容器会存储用户传入值的副本。容器使用 Allocator
模板参数来分配存储的值。
#include <list> #include <assert.h> int main() { std::list<MyClass> myclass_list; MyClass myclass(...); myclass_list.push_back(myclass); //The stored object is different from the original object assert(&myclass != &myclass_list.front()); return 0; }
为了存储新分配的 myclass
副本,容器需要额外的数据:std::list
通常会分配包含指向下一个和上一个节点以及值本身的指针的节点。类似如下:
//A possible implementation of a std::list<MyClass> node class list_node { list_node *next; list_node *previous; MyClass value; };
另一方面,侵入式容器不存储传入对象的副本,而是存储对象本身。将对象插入容器所需的额外数据必须由对象本身提供。例如,要将 MyClass
插入实现链表的侵入式容器,MyClass
必须包含所需的 next 和 previous 指针。
class MyClass { MyClass *next; MyClass *previous; //Other members... }; int main() { acme_intrusive_list<MyClass> list; MyClass myclass; list.push_back(myclass); //"myclass" object is stored in the list assert(&myclass == &list.front()); return 0; }
如我们所见,知道类应该包含哪些额外数据并非易事。Boost.Intrusive 提供了几种侵入式容器,以及一种简单的方法来使用户类与这些容器兼容。
从语义上讲,Boost.Intrusive 容器类似于一个存储对象指针的 STL 容器。也就是说,如果您有一个包含 T
类型对象的侵入式列表,那么 std::list<T*>
将允许您执行基本相同的操作(维护和遍历一组 T 类型及其派生类型的对象)。
非侵入式容器存在一些限制。
std::list<Object*>
。侵入式容器具有一些重要优势。
T*
在 std::list<T*>
中的位置具有线性复杂度)。侵入式容器也有缺点。
表 16.1. 侵入式容器的优点和缺点摘要
问题 |
Intrusive |
非侵入式 |
---|---|---|
内存管理 |
外部 |
通过分配器内部管理 |
插入/删除时间 |
更快 |
更慢 |
内存局部性 |
更好 |
更糟 |
可以将同一个对象插入多个容器 |
是 |
否 |
异常保证 |
更好 |
更糟 |
从值计算迭代器 |
常量 |
非恒定 |
插入/删除可预测性 |
高 |
低 |
内存使用 |
最小 |
超过最小 |
通过值插入对象并保留多态行为 |
是 |
否(切片) |
用户必须修改要插入的值的定义 |
是 |
否 |
容器可复制 |
否 |
是 |
已插入对象的生命周期由...管理 |
用户(更复杂) |
容器(更简单) |
容器不变量可以在不使用容器的情况下被破坏 |
更容易 |
更难(仅适用于指针容器) |
线程安全分析 |
更难 |
更容易 |
有关侵入式和非侵入式容器之间的性能比较,请参阅 性能 部分。