2009-11-08 3 views
0

Мне нужно создать общий несущий объект класс. Я придумал что-то простое, какОбщий класс несущих объектов - C++

template<typename T> 
class ObjectCarrier 
{ 

public: 
    const T& item() const 
    { 
     return item_; 
    } 

    void setItem(T& item) 
    { 
     item_ = item; 
    } 

private: 
    T item_; 
}; 

Это хорошо работает, когда T имеет конструктор по умолчанию (без параметров). Все усложняется, когда T имеет параметризованные конструкторы. Поэтому я переписал класс как

template<typename T> 
class ObjectCarrier 
{ 

public: 
    const T& item() const 
    { 
     return *item_; 
    } 

    void setItem(T& item) 
    { 
     item_ = new T (item); 
    } 

private: 
    T* item_; 
}; 

Измененный переменную item_ в T* и создал новый экземпляр, используя конструктор копирования из T. Снова это сработало, пока T не является типом указателя. Я имею в виду, что ObjectCarrier<Foo*> не будет работать.

Мне интересно, как я могу создать этот класс, чтобы он работал практически для всех типов. Я думаю, мне может понадобиться создать тип traits, специализированный для указателей. Но, к сожалению, я не в состоянии сделать эту работу.

Любая помощь будет отличной.

+1

Почему первый не работает с конструкторами с параметрами? Наверняка вам нужен только конструктор копирования? Или я что-то пропустил? – Goz

+0

@Goz: Вам что-то не хватает. Когда объект создается, по умолчанию (пустой или со всеми параметрами по умолчанию) используется для создания элемента item_. – rmn

ответ

0

Вы можете использовать специализированную специализацию по типу T* и переписать методы в указатели наборов. Вы можете сделать что-то наподобие:

template<typename T> 
class ObjectCarrier<T*> 
{ 
    public: 
    const T* item() const 
    { 
     return item_; 
    } 

    void setItem(T* item) 
    { 
     item_ = item; 
    } 

private: 
    T* item_; 

}; 
+0

Вы имеете в виду специализацию 'ObjectCarrier' для указателей? –

+0

Это нормально для небольших классов, таких как 'ObjectCarrier'. Но разве это единственный вариант? Я думаю, что это будет сложно при создании пользовательских коллекций, которые будут иметь несколько методов. Как STL это реализует? Имеют ли они специализации «вектор» для всех типов? Спасибо за ответ. –

+2

vector требует, чтобы объекты по умолчанию были конструктивными, поэтому эта проблема не возникает. Существует спецификация вектора для вектора для реализации своего рода бит-полей, хотя эта реализация опасна для использования. – Naveen

0

Существует, возможно, относящийся к этому проект дизайн - Memento.

Немного не по теме, но имейте в виду, что как только вы начнете создавать объекты внутри своего класса, вам понадобится способ управления памятью. Я бы предложил использовать std :: auto_ptr как минимум. При использовании std :: auto_ptr вам также потребуется предоставить конструктор копирования и оператор присваивания.

0

Можно было бы провести объект по значению и по-прежнему отложить строительство с использованием размещения нового и что-то вроде следующего:

#include <iostream> 
#include <cassert> 

template <class T> 
class ObjectCarrier 
{ 
public: 
    ObjectCarrier(): ref(0) {} 
    ObjectCarrier(const ObjectCarrier& other): ref(0) 
    { 
     set_data(other.ref); 
    } 
    ~ObjectCarrier() 
    { 
     clear(); 
    } 
    const ObjectCarrier& operator = (const ObjectCarrier& other) 
    { 
     if (other.empty()) 
      clear(); 
     else 
      set_data(other.ref); 
     return *this; 
    } 
    void set(const T& value) 
    { 
     set_value(value); 
    } 
    const T& get() const 
    { 
     assert(!empty() && "No object being carried"); 
     return *ref; 
    } 
    bool empty() const 
    { 
     return ref == 0; 
    } 
    void clear() 
    { 
     if (!empty()) { 
      ref->~T(); 
      ref = 0; 
     } 
    } 
private: 
    char data[sizeof(T)]; 
    T* ref; 
    void set_value(const T& value) 
    { 
     if (!empty()) { 
      *ref = value; 
     } 
     else { 
      ref = new (data) T(value); 
     } 
    } 
    void set_data(const T* value) 
    { 
     if (value) { 
      set_value(*value); 
     } 
    } 
}; 

int main() 
{ 
    ObjectCarrier<int> i; 
    ObjectCarrier<int> j(i); 
    i = j; 
    i.set(10); 
    std::cout << i.get() << '\n'; 
    j = i; 
    i.set(20); 
    std::cout << i.get() << ' ' << j.get() << ' ' << ObjectCarrier<int>(i).get() << '\n'; 
} 

Однако я бы несколько сомнению полезность этого класса. Возможно, единственной целью, которая могла бы быть, было бы действовать как Boost.Optional.

Но если вы не хотите, чтобы класс, чтобы иметь возможность не хранящие значения, просто дать ему параметризованный конструктор:

template<typename T> 
class ObjectCarrier 
{ 

public: 
    ObjectCarrier(const T& value = T()): 
     item_(value) 
    { 
    } 
    const T& item() const 
    { 
     return item_; 
    } 

    void setItem(T& item) 
    { 
     item_ = item; 
    } 

private: 
    T item_; 
}; 

(Это просто, что этот класс кажется довольно бесполезным, если возможно, как фасад для кода, который ожидает, что переменные будут иметь элементы и методы setItem, а не, скажем, оператор присваивания.)

+0

Вашей первой идее может понадобиться намек на проблемы с выравниванием. – sbi

+0

Извините, какие проблемы с выравниванием? – UncleBens

+0

Возможно, мне что-то не хватает, но когда вы пишете 'char data [sizeof (T)]', как вы знаете, массив будет выровнен для объекта типа 'T'? – sbi

2

Вышеуказанные подходы слишком сложны. Держите его простым и просто решайте проблему конструктора arg, используя конструкторы шаблонов. Не используйте указатели, они будут создавать объект жизни и копирование головных болей.

Вот реализация, которую я использую много. Конструкторы шаблонов будут перенаправлять аргументы для вещей непосредственно на вложенный объект, что удобно.Значения оператора T & позволяют передавать carrier<T> функциям типа T без дорогостоящего копирования. Вы можете обернуть объекты, которые принимают до двух аргументов с этим кодом.

/* A wrapper of type T */ 
template <typename T> 
struct carrier { 

    carrier() {} 
    template <typename A1> carrier(const A1& a1) : value(a1) {} 
    template <typename A1, typename A2> carrier(const A1& a1, const A2& a2) : value(a1, a2) {} 

    operator T&() { return value; } 
    operator const T&() const { return value; } 

    T value; 
}; 

Вы можете использовать его как это:

const carrier<point> p1(10,10); // make p1 const to stop people changing it 
showPoint(p1);     // calls a function that expects a point, 
showPoint(p1.value);    // access the point directly 
Смежные вопросы