2008-08-29 54 views
10

無論如何,有一種在C++虛擬靜態成員?在C++中模擬一個類的虛擬靜態成員?

例如:

class BaseClass { 
    public: 
     BaseClass(const string& name) : _name(name) {} 
     string GetName() const { return _name; } 
     virtual void UseClass() = 0; 
    private: 
     const string _name; 
}; 


class DerivedClass : public BaseClass { 
    public: 
     DerivedClass() : BaseClass("DerivedClass") {} 
     virtual void UseClass() { /* do something */ } 
}; 

我知道這個例子很簡單,但如果我有一個將是永遠爲所有派生類相同,但需要複雜數據的矢量從基地訪問類方法?

class BaseClass { 
    public: 
     BaseClass() {} 
     virtual string GetName() const = 0; 
     virtual void UseClass() = 0; 
}; 


class DerivedClass : public BaseClass { 
    public: 
     DerivedClass() {} 
     virtual string GetName() const { return _name; } 
     virtual void UseClass() { /* do something */ } 
    private: 
     static const string _name; 
}; 

string DerivedClass::_name = "DerivedClass"; 

此解決方案不滿足我,因爲我需要在每個類中重新實現成員_name及其訪問器GetName()。就我而言,我有幾個成員跟在_name行爲和派生類的十分之一之後。

有什麼想法?

回答

8

這裏是一個解決方案:

struct BaseData 
{ 
    const string my_word; 
    const int my_number; 
}; 

class Base 
{ 
public: 
    Base(const BaseData* apBaseData) 
    { 
     mpBaseData = apBaseData; 
    } 
    const string getMyWord() 
    { 
     return mpBaseData->my_word; 
    } 
    int getMyNumber() 
    { 
     return mpBaseData->my_number; 
    } 
private: 
    const BaseData* mpBaseData; 
}; 

class Derived : public Base 
{ 
public: 
    Derived() : Base(&sBaseData) 
    { 
    } 
private: 
    static BaseData sBaseData; 
} 

BaseData Derived::BaseData = { "Foo", 42 }; 
2

好像答案是問題 - 你建議的方法似乎是朝着正確的方向走,但如果你有這些共享成員的一個大數目,你可能希望將它們收集到一個結構或類並將其作爲基類構造函數的參數。

如果你堅持具有「共享」成員實施的派生類的靜態成員,您也許能自動生成派生類的代碼。 XSLT是自動生成簡單類的好工具。

一般來說,這個例子並不需要「虛擬靜態」成員,因爲對於這樣的目的,你實際上並不需要繼承 - 相反,你應該使用基類並讓它接受適當的值構造函數 - 可能爲每個「子類型」創建參數的單個實例,並將指針傳遞給它以避免共享數據的重複。另一種類似的方法是使用模板並將作爲模板參數的類傳遞給提供所有相關值的類(這通常稱爲「策略」模式)。

要總結 - 在原來的例子的目的,沒有必要對這種「虛靜態」的成員。如果您仍然認爲您正在編寫的代碼需要它們,請嘗試詳細說明並添加更多上下文。什麼上述我

例子:

class BaseClass { 
    public: 
     BaseClass(const Descriptor& desc) : _desc(desc) {} 
     string GetName() const { return _desc.name; } 
     int GetId() const { return _desc.Id; } 
     X GetX() connst { return _desc.X; } 
     virtual void UseClass() = 0; 
    private: 
     const Descriptor _desc; 
}; 


class DerivedClass : public BaseClass { 
    public: 
     DerivedClass() : BaseClass(Descriptor("abc", 1,...)) {} 
     virtual void UseClass() { /* do something */ } 
}; 

class DerDerClass : public BaseClass { 
    public: 
     DerivedClass() : BaseClass("Wowzer", 843,...) {} 
     virtual void UseClass() { /* do something */ } 
}; 

我想闡述一下這個解決方案,並可能給出一個解決方案,去初始化問題:

一個小的改變,你可以實現上述設計,而不必爲派生類的每個實例創建一個「描述符」的新實例。

您可以創建一個單獨的對象,DescriptorMap,將舉行各描述符的單個實例,並構建派生的對象,像這樣的時候使用它:

enum InstanceType { 
    Yellow, 
    Big, 
    BananaHammoc 
} 

class DescriptorsMap{ 
    public: 
     static Descriptor* GetDescriptor(InstanceType type) { 
      if (_instance.Get() == null) { 
       _instance.reset(new DescriptorsMap()); 
      } 
      return _instance.Get()-> _descriptors[type]; 
     } 
    private: 
     DescriptorsMap() { 
      descriptors[Yellow] = new Descriptor("Yellow", 42, ...); 
      descriptors[Big] = new Descriptor("InJapan", 17, ...) 
      ... 
     } 

     ~DescriptorsMap() { 
      /*Delete all the descriptors from the map*/ 
     } 

     static autoptr<DescriptorsMap> _instance; 
     map<InstanceType, Descriptor*> _descriptors; 
} 

現在我們可以做到這一點:

class DerivedClass : public BaseClass { 
    public: 
     DerivedClass() : BaseClass(DescriptorsMap.GetDescriptor(InstanceType.BananaHammoc)) {} 
     virtual void UseClass() { /* do something */ } 
}; 

class DerDerClass : public BaseClass { 
    public: 
     DerivedClass() : BaseClass(DescriptorsMap.GetDescriptor(InstanceType.Yellow)) {} 
     virtual void UseClass() { /* do something */ } 
}; 

在執行結束時,當C運行時執行uninitializations,它也叫靜態的物體,包括我們的autoptr,這在我們刪除的DescriptorsMap的實例的析構函數。

所以,現在我們有也正在在執行結束時刪除每個描述符的一個實例。請注意,如果派生類的唯一目的是提供相關的「描述符」數據(即與實現虛函數相反),那麼您應該使基類非抽象化,並創建一個每次都有適當的描述符。

1

@Hershi:這種方法的問題是每個派生類的每個實例都有一個數據的副本,這在某種程度上可能很昂貴。也許你可以嘗試這樣的事情(我沒有一個編譯的例子就是吐口水,但這個想法應該很清楚)。


#include <iostream> 
#include <string> 
using namespace std; 

struct DerivedData 
{ 
    DerivedData(const string & word, const int number) : 
    my_word(word), my_number(number) {} 
    const string my_word; 
    const int my_number; 
}; 

class Base { 
public: 
    Base() : m_data(0) {} 
    string getWord() const { return m_data->my_word; } 
    int getNumber() const { return m_data->my_number; } 
protected: 
    DerivedData * m_data; 
}; 


class Derived : public Base { 
public: 
    Derived() : Base() { 
    if(Derived::s_data == 0) { 
     Derived::s_data = new DerivedData("abc", 1); 
    } 
    m_data = s_data; 
    } 
private: 
    static DerivedData * s_data; 
}; 


DerivedData * Derived::s_data = 0; 

int main() 
{ 
    Base * p_b = new Derived(); 
    cout getWord() << endl; 
} 

關於上刪除靜態對象的後續問題:我想到的唯一的解決辦法是使用智能指針,有點像Boost shared pointer

1

我同意Hershi建議使用模板作爲「基類」。從你描述的內容來看,這聽起來更像是模板的使用,而不是子類化。

如下您可以創建一個模板(沒試過編譯此):

 

template <typename T> 
class Object 
{ 
public: 

    Object(const T& newObject) : yourObject(newObject) {} ; 
    T GetObject() const { return yourObject } ; 
    void SetObject(const T& newObject) { yourObject = newObject } ; 

protected: 

    const T yourObject ; 
} ; 

class SomeClassOne 
{ 
public: 

    SomeClassOne(const std::vector& someData) 
    { 
    yourData.SetObject(someData) ; 
    } 

private: 

    Object<std::vector<int>> yourData ; 
} ; 
 

這將讓你使用模板類的方法修改爲從使用自定義類中所需要的數據數據並共享模板類的各個方面。

如果你在使用繼承意圖,那麼你可能不得不求助於你的BaseClass使用一個void *指針和處理鑄造的「喜悅」等

然而,根據你的解釋,似乎你需要模板而不是繼承。

0

聽起來好像你想避免在葉類重複的代碼,所以爲什麼不從基類派生的中間基類。這個中間類可以保存靜態數據,並讓所有的葉子類都從中間基類中派生出來。這假設需要在所有派生類中保存一個靜態數據片段,這在您的示例中看起來如此。