[英]C++ design with static methods
我想用靜態方法定義為類X:
class X
{
static string get_type () {return "X";}
//other virtual methods
}
我想強制繼承自X的類重新定義get_type()方法並返回與“X”不同的字符串(如果他們現在只重新定義get_type,我很高興)。
我該怎么做呢? 我知道我不能擁有虛擬靜態方法。
編輯:問題不是關於type_id,而是關於應該重寫的靜態方法。 例如
class X {
static int getid() {return 1;}
}
template<int id>
class X {
public:
static int getid() { return id; }
};
class Y : public X<2> {
};
您沒有覆蓋該方法,但您已強制每個子類提供ID。 警告:我沒有試過這個,可能有一些微妙的原因導致它不起作用。
如果我沒弄錯,要調用靜態方法,你必須通過指定類的確切名稱來調用方法,例如X::get_type();
, DerivedClass::get_type()
等,在任何情況下,如果在對象上調用,則不考慮對象的動態類型。 因此,至少在特定情況下,當您不期望多態行為時,它可能僅在模板化上下文中有用。
但是,我不明白為什么不應該強制每個有趣的類(繼承或不繼承,因為“編譯時多態”不關心)用模板提供這個功能。 在下面的例子中,您必須專門化get_type
函數,否則您將遇到編譯時錯誤:
#include <string>
struct X {};
struct Derived: X {};
template <class T> std::string get_type() {
static_assert(sizeof(T) == 0, "get_type not specialized for given type");
return std::string();
}
template <> std::string get_type<X>() {
return "X";
}
int main() {
get_type<X>();
get_type<Derived>(); //error
}
( static_assert
是C ++ 0x,否則使用你最喜歡的實現,例如BOOST_STATIC_ASSERT
。如果你對專門化函數感覺不好,請專門化一個結構。如果你想強迫一個錯誤,如果有人不小心試圖將它專門化為未派生的類型從X開始,那對於type_traits
也應該是可行的。)
我會說你知道原因,但以下是一個很好的解釋:
看起來你必須設計自己的方式。 也許是一個包裹Singleton的虛函數?
不要那樣做,而是使用typeid
。
長話短說,你做不到。 要求派生類覆蓋基類函數的唯一方法是使其成為純虛擬(不能是靜態的)。
出於多種原因,您無法做到這一點。 您無法在X中定義該函數並使其成為純虛函數。 您根本無法擁有虛擬靜態功能。
為什么它們必須是靜態的?
干得好
class X
{
static string get_type() {return "X"; }
};
class Y : public X
{
static string get_type() {return "Y"; }
};
上面的代碼完全符合您的要求:派生類重新定義get_type
並返回不同的字符串。 如果這不是你想要的,你必須解釋原因。 您必須解釋您正在嘗試做什么以及您對該靜態方法的期望。 如果您的原始問題絕對不清楚。
您提到了一些關於保證子類型為您的函數生成唯一值的地方。 正如其他人所說的那樣,這在編譯時是不可能的[至少在沒有使用可能或可能不可接受的模板的情況下]。 但是如果你把它推遲到運行時間,你可以拉掉類似的東西。
class Base {
static std::vector<std::pair<const std::type_info*, int> > datas;
typedef std::vector<std::pair<const std::type_info*, int> >::iterator iterator;
public:
virtual ~Base() { }
int Data() const {
const std::type_info& info = typeid(*this);
for(iterator i = datas.begin(); i != datas.end(); ++i)
if(*(i->first) == info) return i->second;
throw "Unregistered Type";
}
static bool RegisterClass(const Base& p, int data) {
const std::type_info& info = typeid(p);
for(iterator i = datas.begin(); i != datas.end(); ++i) {
if(i->second == data) {
if(*(i->first) != info) throw "Duplicate Data";
return true;
}
if(*(i->first) == info) throw "Reregistering";
}
datas.push_back(std::make_pair(&info, data));
return true;
}
};
std::vector<std::pair<const std::type_info*, int> > Base::datas;
class Derived : public Base { };
const DerivedRegisterFlag = Base::RegisterClass(Derived(), 10);
class OtherDerived : public Base { };
const OtherDerivedRegisterFlag = Base::RegisterClass(OtherDerived(), 10); //exception
注意事項:這是完全未經測試的。 如果以這種方式執行,則在進入main之前會拋出異常。 您可以將注冊移動到構造函數中,並且如果您願意,則接受注冊檢查的每個實例開銷。
為簡單起見,我選擇了無序向量; 我不確定type_info::before
提供了必要的語義來用作地圖的謂詞,並且可能你不會有這么多的派生類,線性搜索無論如何都會有問題。 我存儲指針因為你不能直接復制type_info
對象。 這大多是安全的,因為typeid
返回的對象的生命周期是整個程序。 程序關閉時可能會出現問題,我不確定。
我沒有嘗試防止初始化錯誤的靜態順序。 如上所述,這將在某些時候失敗。
最后,不是它不是靜態的,但無論如何,“靜態”和“虛擬”並不是真的有意義。 如果您沒有要處理的類型實例,那么您如何知道選擇哪種覆蓋方法? 在某些情況下,模板中您可能合法地想要在沒有實際對象的情況下調用靜態方法,但這可能不常見。
*編輯:另外,我不確定這是如何與動態鏈接庫等交互的。 我懷疑在這些情況下RTTI是不可靠的,所以顯然這同樣不可靠。
使用Delphi,它支持類上的虛擬靜態成員。 ;>
抱怨復活這個話題,但我剛剛遇到了這個道德危機。 這是一個非常大膽且可能是愚蠢的陳述,但我完全不同意大多數人所說的static virtual
沒有任何意義。 這種困境源於靜態成員如何被普遍使用而不是他們實際在底下做什么。
人們經常使用靜態類和/或成員來表達事實 - 如果實例是相關的,則對所有實例都是正確的,或者在靜態類的情況下僅僅是關於世界的事實。 假設您正在為Philosophy類建模。 你可以定義abstract class Theory
來代表一種要教授的理論,然后繼承Theory
, TheoryOfSelf
, TheoryOfMind
和其他Theory
。 要教一個理論,你真的需要一種名為express()
的方法,它使用適合觀眾的特定詞組來表達理論。 可以假設任何繼承類都應該公開一個相同的方法express()
。 如果我能夠,我會用模擬這種關系static virtual Theory.express()
-它既是一個超越實例(因此靜態)和非特異性的概念陳述事實,要求每種類型理論(因此具體的實施虛擬)。
然而,我完全同意人們以static
實際上正在做的為由來證明禁令 - 它在編碼原則方面是完全合理的,這個問題源於人們通常模擬現實世界的習慣方式。
我能夠想到的這個問題的最佳解決方案是將Theory
模型化為單身 - 可能有一個理論的實例 ,但只有其中一個。 如果你想要一個替代品,它是一個不同的類型,所以創建一個新的派生類。 對我來說,這種方法看似隨意,並引入了不必要的噪音
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.