[英]Simulating a virtual static member of a class in 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行為和十分之一的派生類。
任何的想法?
這是一個解決方案:
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 };
似乎問題的答案就在於問題 - 您建議的方法似乎是正確的方向,除非您有大量的共享成員,您可能希望將它們收集到結構或類中,然后將其作為基類構造函數的參數。
如果您堅持將“共享”成員實現為派生類的靜態成員,則可以自動生成派生類的代碼。 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運行時執行未初始化時,它還會調用靜態對象的析構函數,包括我們的autoptr,它會刪除我們的DescriptorsMap實例。
所以現在我們有一個每個描述符的實例,在執行結束時也會被刪除。
請注意,如果派生類的唯一目的是提供相關的“描述符”數據(即與實現虛函數相反),那么您應該將基類設為非抽象,並且只需使用適當的創建實例描述每次。
@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共享指針 。
我同意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 *指針並處理強制轉換等的“樂趣”。
但是,根據您的解釋,您似乎需要模板而不是繼承。
聽起來好像你試圖避免在葉子類中復制代碼,所以為什么不從基類派生一個中間基類。 這個中間類可以保存靜態數據,並且所有葉類都派生自中間基類。 這預示着需要在所有派生類上保存一個靜態數據,這從您的示例中可以看出來。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.