Как переписать поведение по умолчанию метода конструкции в классе распределителя в C ++ STL - PullRequest
2 голосов
/ 11 ноября 2011

Как переписать поведение по умолчанию метода конструкции в классе распределителя в 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

Ответы [ 2 ]

11 голосов
/ 11 ноября 2011

Добро пожаловать в удивительный мир распределителей.Надеюсь, вам понравится ваше пребывание, хотя это маловероятно.

Правило № 1: не выводите из std::allocator.Если вы хотите использовать собственную схему размещения, напишите свой собственный распределитель.Если вы хотите «переопределить» некоторые функции в std :: allocator, просто создайте экземпляр std::allocator и вызовите его функции в не переопределенных функциях.

Обратите внимание, что получение на самом деле не работает в любом случае.В C ++ 03 распределителям не разрешено иметь состояние, а указатель v-таблицы считается состоянием.Поэтому распределители не могут иметь виртуальные функции.Вот почему std::allocator не имеет виртуальных функций.

Правило # 2: std::list<T> никогда выделяет T объектов.Помните: std::list является связанным списком.Он выделяет узлов , которые имеют T в качестве члена.Он делает это с помощью некоторой магии шаблона, где он вызывает ваш класс итератора, используя в качестве параметра его внутренний тип узла, и возвращает новый объект-распределитель того же шаблона, но с другим параметром шаблона.

это через член структуры шаблона вашего вызова распределителя rebind, который имеет typedef члена с именем other, который определяет новый тип распределителя.В вашем случае std::list сделает это:

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

И это все еще предоставляется базовым классом.Таким образом, тип MyAlloc::rebind<_ListInternalNodeType>::other равен std::allocator<_ListInternalNodeType>.Какой тип распределителя будет использовать std::list для фактического распределения вещей.

4 голосов
/ 11 ноября 2011

Вы должны сделать немного больше, чем то, что вы делаете в своем коде.Это минимальный код, необходимый для того, чтобы заставить его работать так, как вы хотите:

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

Вся идея этого минимального кода заключается в переопределении определения rebind шаблона класса в базовом классе std::allocator, которыйопределяется как:

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

, когда на самом деле нам это нужно:

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

Поскольку в конечном итоге это rebind<U>::other, который используется в качестве распределителя.

Кстати, typedefs необходимы, чтобы привести имена (типов) в область видимости производного класса (по умолчанию они не видны, так как 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...