2011-11-11 43 views
2

如何覆蓋STL中allocator類中構造方法的默認行爲?以下似乎不工作:如何覆蓋C++中allocator類中構造方法的默認行爲STL

#include <list> 
#include <iostream> 
#include <memory> 

struct MyObj { 
    MyObj() { 
     std::cout << "This is the constructor" << std::endl; 
    } 
    MyObj(const MyObj& x) { 
     std::cout << "This is the copy constructor" << std::endl; 
    } 
}; 

class MyAlloc : public std::allocator <MyObj>{ 
public: 
    void construct(pointer p, const_reference t){ 
     std::cout << "Construct in the allocator" << std::endl; 
     new((void*)p) MyObj(t); 
    } 
}; 

int main(){ 
    MyObj x;   
    std::list <MyObj,MyAlloc> list(5,x); 
} 

該程序返回

This is the constructor 
This is the copy constructor 
This is the copy constructor 
This is the copy constructor 
This is the copy constructor 
This is the copy constructor 

我想它

This is the constructor 
Construct in the allocator 
This is the copy constructor 
Construct in the allocator 
This is the copy constructor 
Construct in the allocator 
This is the copy constructor 
Construct in the allocator 
This is the copy constructor 
Construct in the allocator 
This is the copy constructor 

回答

11

歡迎回到分配器的精彩世界。我希望你喜歡你的逗留,儘管這不太可能。

規則1:不要從std::allocator派生。如果你想使用你自己的分配方案,那麼編寫你自己的分配器。如果您想要「覆蓋」std :: allocator中的某些功能,只需創建一個std::allocator實例並在未覆蓋的函數中調用其功能。

請注意,推導並不真正工作。在C++ 03中,分配器不允許有狀態,並且一個V表指針作爲狀態計數。所以分配器不能具有虛擬功能。這就是爲什麼std::allocator沒有虛擬功能。

規則#2:std::list<T>從來沒有分配T對象。記住:std::list是一個鏈接列表。它分配節點,其中有一個T作爲成員。它通過一些模板魔術來做到這一點,它使用它的內部節點類型作爲參數來調用你的迭代器類,並且返回同一個模板的新分配器對象,但是具有不同的模板參數。

它通過分配器調用rebind的模板struct成員完成此操作,該成員的typedef名爲other,它定義了新的分配器類型。在你的情況下,std::list將這樣做:

MyAlloc::rebind<_ListInternalNodeType>::other theAllocatorIWillActuallyUse(); 

而這仍然是由基類提供。所以MyAlloc::rebind<_ListInternalNodeType>::other的類型是std::allocator<_ListInternalNodeType>std::list將用於實際分配事物的分配器類型。

+0

+1。非常好!... – Nawaz

+2

所以@NicolBolas說'不從'std :: allocator'派生',但@Nawaz'的答案正是如此。這是一個有效的,受支持的事情嗎?或者如果我嘗試它,宇宙理論上可以變成豆芽味的果凍嗎? 忽略該虛擬分派不可用(因爲當提供派生類型作爲模板參數時,基類型是非實質性的且不需要虛擬分派)並且忽略派生的設計優點作爲繼承實現的機制。 – boycy

4

你必須做的比你在代碼中做的更多。這是這是必要的,以最小的代碼,使其工作,你希望它的方式:

template<typename T> 
class MyAlloc : public std::allocator <T> 
{ 
public: 
    typedef size_t  size_type; 
    typedef ptrdiff_t difference_type; 
    typedef T*   pointer; 
    typedef const T* const_pointer; 
    typedef T&   reference; 
    typedef const T& const_reference; 
    typedef T   value_type; 


    template<typename U> 
    struct rebind 
    { 
     typedef MyAlloc <U> other; 
    }; 

    MyAlloc() {} 

    template<typename U> 
    MyAlloc(const MyAlloc<U>&) {} 

    void construct(pointer p, const_reference t){ 
     std::cout << "Construct in the allocator" << std::endl; 
     new((void*)p) MyObj(t); 
    } 

}; 

,然後用它作爲:

int main(){ 
    MyObj x;   
    std::list <MyObj,MyAlloc<MyObj> > list(5,x); 
} 

輸出(根據您的需要):

This is the constructor 
Construct in the allocator 
This is the copy constructor 
Construct in the allocator 
This is the copy constructor 
Construct in the allocator 
This is the copy constructor 
Construct in the allocator 
This is the copy constructor 
Construct in the allocator 
This is the copy constructor 

在線演示:http://www.ideone.com/QKdqm

的這個最小碼的整體思路是重寫defin在基類std::allocatorrebind類模板被定義爲銀行足球比賽:

template<typename U> 
struct rebind 
{ 
    typedef std::allocator<U> other; 
}; 

的時候,其實我們需要這樣的:

template<typename U> 
struct rebind 
{ 
    typedef MyAlloc<U> other; 
}; 

因爲最終它是rebind<U>::other被用作分配器。

順便說一下,類型定義對於將派生類的範圍中的名稱(類型)(默認情況下它們不可見,因爲MyAlloc現在是類模板)是必需的。所以,你可以寫爲:

template<typename T> 
class MyAlloc : public std::allocator <T> 
{ 
    typedef std::allocator <T> base; 
public: 
    typedef typename base::size_type  size_type; 
    typedef typename base::difference_type difference_type; 
    typedef typename base::pointer   pointer; 
    typedef typename base::const_pointer const_pointer; 
    typedef typename base::reference  reference; 
    typedef typename base::const_reference const_reference; 
    typedef typename base::value_type  value_type; 

    //same as before 
}; 

其結果將是相同的:http://www.ideone.com/LvQhI