2014-09-29 48 views
2

實現我有一個可以一般持有任何(原語)一類類型:泛型類型的容器使用模板

class Value 
{ 
    private: 
     int    i_value; 
     unsigned int ui_value; 
     long   l_value; 
     unsigned long ul_value; 
     short   s_value; 
     float   f_value; 
     double   d_value; 
     char   c_value; 
     bool   b_value; 
     std::string  str_value; 

     int type; 

     void setValue(int value); 
     void setValue(unsigned int value); 
     void setValue(long value); 
     void setValue(unsigned long value); 
     void setValue(short value); 
     void setValue(float value); 
     void setValue(double value); 
     void setValue(char value); 
     void setValue(bool value); 
     void setValue(std::string value); 

    public: 
     Value(int value); 
     Value(unsigned int value); 
     Value(long value); 
     Value(unsigned long value); 
     Value(short value); 
     Value(float value); 
     Value(double value); 
     Value(char value); 
     Value(bool value); 
     Value(std::string value); 

     Value(Value& other); //Copy Constructor 
     ~Value(); 

     int getType(); 
     std::string toString(int format); 

}; 

這是很好的,因爲我可以這樣做:

Value * v1 = new Value(55); 
Value * v2 = new Value(1.2); 
Value * v3 = new Value("yes"); 
Value * v4 = new Value(true); 

然而,正如你所看到的,這很醜陋。超重的一切,使其工作。我認爲模板可以使這個通用。但是,據我所知,你總是必須指定類型,這種類型會挫敗課程的全部目的。

例如:

Value<int> * v1 = new Value<int>(55); 
Value<double> * v2 = new Value<double>(1.2); 
Value<string> * v3 = new Value<string>("yes"); 
Value<bool> * v4 = new Value<bool>(true); 

如果我使用的模板,我不能再這樣做vector<Value *>我就像之前。這是正確的,還是我錯過了模板的某些方面可以幫助在這種情況下?

+0

如果要使用可能具有不同類型值的向量,則不能使用模板。假設一次只有一個值類型處於活動狀態,則應該使用未使用的類型來查看工會與相同的內存。 – 2014-09-29 16:27:24

+3

請參閱'boost :: any' – matsjoyce 2014-09-29 16:28:24

+2

@matsjoyce或'boost :: variant',瞭解有限的已知類型。 – dyp 2014-09-29 16:31:33

回答

3

所有你需要的是一個模板父類:

class BaseValue 
{ 
public: 
    virtual ~BaseValue() 
    {} 
}; 

template<typename T> 
class Value : public BaseValue 
{ 
public: 
    Value(const T& value) 
     :m_value(value) 
    {} 
    void set(const T& value) 
    { 
     m_value = value; 
    } 
    const T& get() 
    { 
     return m_value; 
    } 
    virtual ~Value() 
    {} 
private: 
    T m_value; 
}; 

std::vector<BaseValue*> values; 
values.push_back(new Value<int>(1)); // int 
values.push_back(new Value<double>(1.0)); // double 
values.push_back(new Value<char*>("asdf")); // pointer to array on stack :(
values.push_back(new Value<char>('c')); // char 
+0

我沒有想到這一點。當你將它推入矢量中時,你仍然需要指定類型,但至少現在(在我看來)模板比以前更可行。謝謝! – RPGillespie 2014-09-29 17:35:49

1

當然你也可以做模板,但你也需要做多態。

class StupidAndEmpty {} // add virtual destructor if Value needs a destructor 

template<class dType> 
Value : StupidAndEmpty { 
// do smart things with dType 
} 

vector<StupidAndEmpty *> notSoStupid; 

唯一的問題是你使用它們,當你讓他們從矢量回來怎麼辦。

1

第一個問題:使用模板工廠函數而不是新的。

第二個問題:使用一個通用的基類。

爲了能夠正確刪除矢量上的指針所指向的對象,您需要一個虛擬析構函數。另外,爲了做指向基類的指針,你需要在基類中使用虛擬方法。

例子:

class ValueBase 
{ 
public: 
    virtual ~ValueBase() = default; 
    virtual void Print(std::ostream & os) const = 0; 
}; 

std::ostream & operator<< (std::ostream & os, const ValueBase & value) 
{ 
    value.Print(os); 
    return os; 
} 

template<typename T> class Value : public ValueBase 
{ 
    T value; 
public: 
    Value(const T & v) : value(v) {} 
    const T & Get() const; 
    void Set(const T & v); 
    void Print(std::ostream & os) const 
    { 
     os << value; 
    } 
    // ... 
}; 

template<typename T> Value<T> * NewValue(const T & v) 
{ 
    return new Value<T>(v); 
} 

現在你可以做

ValueBase * v1 = NewValue(55); 
ValueBase * v2 = NewValue(1.2); 
ValueBase * v3 = NewValue<std::string>("yes"); 
ValueBase * v4 = NewValue(true); 

std::vector<ValueBase *> vec; 

vec.push_back(v1); 
vec.push_back(v2); 
vec.push_back(v3); 
vec.push_back(v4); 
vec.push_back(NewValue(2350983444325345ll)); 

for (const auto & entry : vec) 
{ 
    std::cout << *entry << " "; 
} 
std::cout << "\n"; 

注意,通常並不需要的NewValue的模板參數,因爲它會推斷。與"yes"這不起作用,因爲Value<T>將實例化與T = char [4]這將要求您在構造函數中使用strcpy。我發現很明顯地表明需要如上所述的轉換。如果你喜歡這種含蓄,做一個過載:

Value<std::string> * NewValue(const char * v) 
{ 
    return new Value<std::string>(v); 
} 

確保刪除內容手動

for (const auto & entry : vec) 
{ 
    delete entry; 
} 
vec.clear(); 

或使用std::unique_ptr代替裸指針:

template<typename T> std::unique_ptr<Value<T>> UniqueValue(const T & v) 
{ 
    return std::unique_ptr<Value<T>>(new Value<T>(v)); 
} 

std::vector<std::unique_ptr<ValueBase>> vec; 
vec.push_back(NewValue(4.5)); 

如果擴展Value事實證明,你需要在析構函數中做一些事情,你必須實現複製構造函數,賦值運算符以及可能的移動構造函數和移動賦值運算符。 (「三大規則」或「五大規則」)。)在上述版本中,「零規則」仍然適用,因爲析構函數仍然與隱式定義的一樣(= default)。

如果添加析構函數,它應該是虛擬的。否則,如果刪除VirtualBase指針,則可能會發生內存泄漏。