Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

How to use Boost.Intrusive 如何使用 Boost.Intrusive

Using base hooks 使用基類鉤子
Using member hooks 使用成員鉤子
Using both hooks 使用兩種鉤子
Object lifetime 對象的生存期

If you plan to insert a class in an intrusive container, you have to make some decisions influencing the class definition itself. Each class that will be used in an intrusive container needs some appropriate data members storing the information needed by the container. We will take a simple intrusive container, the intrusive list (boost::intrusive::list), for the following examples, but all Boost.Intrusive containers are very similar. To compile the example using boost::intrusive::list, just include:
如果你計劃將一個類插入到介入式容器中,你必須作出一些影響這個類本身的定義的決定。每一個要在介入式容器中使用的類都需要一些適當的數據成員,來保存容器所需的信息。以下例子,我們將看到一個簡單的介入式容器:介入式鏈表(boost::intrusive::list),不過所有的 Boost.Intrusive 容器都非常相似。要編譯使用 boost::intrusive::list 的例子,只要包含:

#include <boost/intrusive/list.hpp>

Every class to be inserted in an intrusive container, needs to contain a hook that will offer the necessary data and resources to be insertable in the container. With Boost.Intrusive you just choose the hook to be a public base class or a public member of the class to be inserted.
每一個被插入到介入式容器中的類,為了可以插入到容器,都要包含一個鉤子,它提供了必要的數據和資源。對於 Boost.Intrusive,你只需選擇是用被插入類的公有基類,還是被插入類的公有成員來實現鉤子。

For list, you can publicly derive from list_base_hook.
對於 list,你可以公有派生自 list_base_hook.

template <class ...Options>
class list_base_hook;

The class can take several options. Boost.Intrusive classes receive arguments in the form option_name<option_value>. You can specify the following options:
該類可以接受幾個選項。Boost.Intrusiveoption_name<option_value> 的形式來接受實參。你可以指定以下選項:

  • tag<class Tag>: this argument serves as a tag, so you can derive from more than one list_base_hook and hence put an object in multiple intrusive lists at the same time. An incomplete type can serve as a tag. If you specify two base hooks, you must specify a different tag for each one. Example: list_base_hook< tag<tag1> >. If no tag is specified a default one will be used (more on default tags later).
    tag<class Tag>: 該實參被作為一個標記,這樣你就可以派生自多個 list_base_hook,從而將一個對像同時放入多個介入式鏈表。一個不完整的類型就可以作為標記。如果你指定了兩個基類鉤子,你必須為每一個指定不同的標記。例如:list_base_hook< tag<tag1> >。如果未指定標記,則使用一個缺省值(稍後對缺省標記再作說明)。
  • link_mode<link_mode_type LinkMode>: The second template argument controls the linking policy. Boost.Intrusive currently supports 3 modes: normal_link, safe_link and auto_unlink. By default, safe_link mode is used. More about these in sections Safe hooks and Auto-unlink hooks. Example: list_base_hook< link_mode<auto_unlink> >
    link_mode<link_mode_type LinkMode>: 第二個模板實參控制鏈接策略。目前 Boost.Intrusive 支持3種模式:normal_link, safe_linkauto_unlink。缺省使用 safe_link 模式。更多說明在 安全鉤子自斷鉤子 章節。例如:list_base_hook< link_mode<auto_unlink> >
  • void_pointer<class VoidPointer>: this option is the pointer type to be used internally in the hook. The default value is void *, which means that raw pointers will be used in the hook. More about this in the section titled Using smart pointers with Boost.Intrusive containers. Example: list_base_hook< void_pointer< my_smart_ptr<void> >
    void_pointer<class VoidPointer>: 這一選項是鉤子內部所使用的指針類型。缺省值為 void *,這意味著在鉤子中將使用裸指針。更多說明在 在 Boost.Intrusive 容器中使用智能指針 一節。例子:list_base_hook< void_pointer< my_smart_ptr<void> >

For the following examples, let's forget the options and use the default values:
以下例子中,讓我們忘記這些選項,只使用缺省值:

#include <boost/intrusive/list.hpp>

using namespace boost::intrusive;

class Foo
   //Base hook with default tag, raw pointers and safe_link mode
//帶缺省標記、裸指針和 safe_link 模式的基類鉤子
: public list_base_hook<> { /**/ };

After that, we can define the intrusive list:
然後,我們就可以定義介入式鏈表:

template <class T, class ...Options>
class list;

list receives the type to be inserted in the container (T) as the first parameter and optionally, the user can specify options. We have 3 option types:
list 將被插入到容器中的類型(T) 作為第一個參數,並接受其它由用戶指定的可選項。我們有3個選項類型:

  • base_hook<class Hook> / member_hook<class T, class Hook, Hook T::* PtrToMember> / value_traits<class ValueTraits>: All these options specify the relationship between the type T to be inserted in the list and the hook (since we can have several hooks in the same T type). member_hook will be explained a bit later and value_traits will be explained in the Containers with custom ValueTraits section. If no option is specified, the container will be configured to use the base hook with the default tag. Some options configured for the hook (the type of the pointers, link mode, etc.) will be propagated to the container.
    base_hook<class Hook> / member_hook<class T, class Hook, Hook T::* PtrToMember> / value_traits<class ValueTraits>: 所有這些選項都是指定插入到鏈表中的類型 T 與鉤子之間的關係(因為對於同一個類型 T,我們可以有多個鉤子)。member_hook 稍後解釋,value_traits 則將在 帶定制化 ValueTraits 的容器 一節中解釋。如果未指定選項,容器將被配置為使用帶缺省標記的基類鉤子。為鉤子所配置的選項(指針類型、鏈接模式等等)將被傳到容器。
  • constant_time_size<bool Enabled>: Specifies if a constant time size() function is demanded for the container. This will instruct the intrusive container to store an additional member to keep track of the current size of the container. By default, contant-time size is activated.
    constant_time_size<bool Enabled>: 指定容器是否需要一個常量時間的 size() 函數。它將通知介入式容器保存一個額外成員,以跟蹤容器的當前大小。缺省情況下,常量時間大小被激活。
  • size_type<bool Enabled>: Specifies a type that can hold the size of the container. This type will be the type returned by list.size() and the type stored in the intrusive container if constant_time_size<true> is requested. The user normally will not need to change this type, but some containers can have a size_type that might be different from std::size_t (for example, STL-like containers use the size_type defined by their allocator). Boost.Intrusive can be used to implement such containers specifying the the type of the size. By default the type is std::size_t.
    size_type<bool Enabled>: 指定一個可以保存容器大小的類型。如果 constant_time_size<true> 被請求,則該類型將作為 list.size() 的返回類型並保存在介入式容器中。用戶通常不需要修改這一類型,不過有些容器可能具有一個不同於 std::size_t 的 size_type (例如,類-STL容器使用由它們的分配器所定義的 size_type)。Boost.Intrusive 可以用於實現指定大小類型的容器。該類型的缺省值為 std::size_t.

Example of a constant-time size intrusive list that will store Foo objects, using the base hook with the default tag:
以下例子是一個保存 Foo 對象,使用基類鉤子,帶缺省標記,含常量時間大小函數的介入式鏈表:

typedef list<Foo> FooList;

Example of a intrusive list with non constant-time size that will store Foo objects:
以下例子是一個保存 Foo 對象,不含常量時間大小函數的介入式鏈表:

typedef list<Foo, constant_time_size<false> > FooList;

Remember that the user must specify the base hook if the base hook has no default tag (e.g: if more than one base hook is used):
請記住,如果基類鉤子沒有缺省標記,用戶必須指定該基類鉤子(如:如果有一個以上的基類鉤子):

#include <boost/intrusive/list.hpp>

using namespace boost::intrusive;

struct my_tag;

typedef list_base_hook< tag<my_tag> > BaseHook;
class Foo   :  public BaseHook
{ /**/ };

typedef list< Foo, base_hook<BaseHook> > FooList;

Once the list is defined, we can use it:
定義了該鏈表後,我們就可以使用它了:

//An object to be inserted in the list 插入到鏈表中的對象
Foo foo_object; FooList list; list.push_back(object); assert(&list.front() == &foo_object);

Sometimes an 'is-a' relationship between list hooks and the list value types is not desirable. In this case, using a member hook as a data member instead of 'disturbing' the hierarchy might be the right way: you can add a public data member list_member_hook<...> to your class. This class can be configured with the same options as list_base_hook except the option tag:
有時候,鏈表鉤子和鏈表值類型之間的 'is-a' 關係是不合理的。這時,使用一個成員鉤子作為數據成員,而不是'打亂'類的繼承層次,可能是正確的方法:你可增加一個公有數據成員 list_member_hook<...> 到你的類中。該類可以用和 list_base_hook 一樣的選項來配置,除了 tag 選項:

template <class ...Options>
class list_member_hook;

Example:
例如:

#include <boost/intrusive/list.hpp>

class Foo 
{ public: list_member_hook<> hook_;
//...
};

When member hooks are used, the member_hook option is used to configure the list:
使用成員鉤子時,member_hook 選項被用於配置鏈表:

//This option will configure "list" to use the member hook 這個選項將 "list" 配置為使用成員鉤子
typedef member_hook<Foo, list_member_hook<>, &Foo::hook_> MemberHookOption; //This list will use the member hook 這個 list 將使用成員鉤子
typedef list<Foo, MemberHookOption> FooList;

Now we can use the container:
現在我們就可以使用這個容器了:

//An object to be inserted in the list 被插入到鏈表中的對象
Foo foo_object; FooList list; list.push_back(object); assert(&list.front() == &foo_object);

You can insert the same object in several intrusive containers at the same time, using one hook per container. This is a full example using base and member hooks:
你可以將同一個對像同時插入到多個介入式容器中,每個容器使用一個鉤子。以下是一個使用基類鉤子和成員鉤子的完整例子:

#include <boost/intrusive/list.hpp>
#include <vector>

using namespace boost::intrusive;

class MyClass : public list_base_hook<>
{
   int int_;

   public:
   list_member_hook<> member_hook_;

   MyClass(int i) :  int_(i)  {}
};

//Define a list that will store MyClass using the base hook 定義一個鏈表,使用基類鉤子保存 MyClass
typedef list<MyClass> BaseList; //Define a list that will store MyClass using the member hook 定義一個鏈表,使用成員鉤子保存 MyClass
typedef member_hook < MyClass, list_member_hook<>, &MyClass::member_hook_> MemberOption; typedef list<MyClass, MemberOption> MemberList; int main() { typedef std::vector<MyClass>::iterator VectIt; typedef std::vector<MyClass>::reverse_iterator VectRit; //Create several MyClass objects, each one with a different value 創建幾個 MyClass 對象,各有不同的值
std::vector<MyClass> values; for(int i = 0; i < 100; ++i) values.push_back(MyClass(i)); BaseList baselist; MemberList memberlist; //Now insert them in the reverse order in the base hook list 現在將它們以反序插入到基類鉤子鏈表中
for(VectIt it(values.begin()), itend(values.end()) ; it != itend ; ++it){ baselist.push_front(*it); } //Now insert them in the same order as in vector in the member hook list 現在將它們以相同順序插入到成員鉤子鏈表中
for(VectIt it(values.begin()), itend(values.end()); it != itend; ++it) memberlist.push_back(*it); //Now test lists 現在測試這些鏈表
{ BaseList::reverse_iterator rbit(baselist.rbegin()), rbitend(baselist.rend()); MemberList::iterator mit(memberlist.begin()), mitend(memberlist.end()); VectIt it(values.begin()), itend(values.end()); //Test the objects inserted in the base hook list 測試插入到基類鉤子鏈表中的對象
for(; it != itend; ++it, ++rbit) if(&*rbit != &*it) return 1; //Test the objects inserted in the member hook list 測試插入到成員鉤子鏈表中的對象
for(it = values.begin(); it != itend; ++it, ++mit) if(&*mit != &*it) return 1; } return 0; }

Even if the interface of list is similar to std::list, its usage is a bit different: You always have to keep in mind that you directly store objects in intrusive containers, not copies. The lifetime of a stored object is not bound to or managed by the container:
雖然 list 的接口與 std::list 類似,但是它的用法有些不一樣:你必須緊記你是直接將對像保存到介入式容器中,而不是其拷貝。被保存對象的生存期未被綁定到容器,也不是由容器負責管理:

  • When the container gets destroyed before the object, the object is not destroyed, so you have to be careful to avoid resource leaks.
    如果容器在對像之前被銷毀,則對像不會被銷毀,因此你必須小心以防止資源洩漏。
  • When the object is destroyed before the container, your program is likely to crash, because the container contains a pointer to an non-existing object.
    如果對像在容器之前被銷毀,你的程序很可能會崩潰,因為容器中包含了一個指針,指向不存在的對象。

PrevUpHomeNext