Discussion: Use a factory function if you need "virtual behavior" during initialization
討論:若是在初始化期間須要「虛行爲」,請使用工廠函數
If your design wants virtual dispatch into a derived class from a base class constructor or destructor for functions like f and g, you need other techniques, such as a post-constructor -- a separate member function the caller must invoke to complete initialization, which can safely call f and g because in member functions virtual calls behave normally. Some techniques for this are shown in the References. Here's a non-exhaustive list of options:git
若是您想要爲f和g之類的函數設計從基類構造函數或析構函數到派生類的虛分發,則須要其餘技術,例如後構造函數-調用者必須調用一個單獨的成員函數才能完成初始化,能夠安全地調用f和g,由於在成員函數中,虛擬調用的行爲正常。參考文獻中顯示了一些用於此目的的技術。如下是非詳盡的選項列表:github
Pass the buck: Just document that user code must call the post-initialization function right after constructing an object.web
轉移責任:只需說明用戶代碼在構造對象後必須當即調用初始化後的函數。
編程Post-initialize lazily: Do it during the first call of a member function. A Boolean flag in the base class tells whether or not post-construction has taken place yet.設計模式
延遲後初始化:在成員函數的第一次調用期間執行此操做。基類中的布爾值標誌指示是否進行了後期構造。
安全Use virtual base class semantics: Language rules dictate that the constructor most-derived class decides which base constructor will be invoked; you can use that to your advantage. (See [Taligent94].)微信
使用虛擬基類語義:語言規則規定,最(後,譯者注)派生類的構造函數決定將調用哪一個基類構造函數;您能夠利用它來發揮本身的優點。(請參閱[Taligent94]。)
架構Use a factory function: This way, you can easily force a mandatory invocation of a post-constructor function.app
使用工廠函數:這樣,您能夠輕鬆強制強制調用後構造函數。
ide
Here is an example of the last option:
這是最後一個選項的示例:
class B {
public:
B()
{
/* ... */
f(); // BAD: C.82: Don't call virtual functions in constructors and destructors
/* ... */
}
virtual void f() = 0;
};
class B {
protected:
class Token {};
public:
// constructor needs to be public so that make_shared can access it.
// protected access level is gained by requiring a Token.
explicit B(Token) { /* ... */ } // create an imperfectly initialized object
virtual void f() = 0;
template<class T>
static shared_ptr<T> create() // interface for creating shared objects
{
auto p = make_shared<T>(typename T::Token{});
p->post_initialize();
return p;
}
protected:
virtual void post_initialize() // called right after construction
{ /* ... */ f(); /* ... */ } // GOOD: virtual dispatch is safe
}
};
class D : public B { // some derived class
protected:
class Token {};
public:
// constructor needs to be public so that make_shared can access it.
// protected access level is gained by requiring a Token.
explicit D(Token) : B{ B::Token{} } {}
void f() override { /* ... */ };
protected:
template<class T>
friend shared_ptr<T> B::create();
};
shared_ptr<D> p = D::create<D>(); // creating a D object
This design requires the following discipline:
此設計須要遵循如下原則:
Derived classes such as D must not expose a publicly callable constructor. Otherwise, D's users could create D objects that don't invoke post_initialize.
諸如D之類的派生類不得公開可調用的構造函數。不然,D的用戶能夠建立不調用post_initialize的D對象。
Allocation is limited to operator new. B can, however, override new (see Items 45 and 46 in SuttAlex05).
分配僅限於new運算符。可是,B能夠覆蓋new(請參見SuttAlex05中的項目45和46)。
D must define a constructor with the same parameters that B selected. Defining several overloads of create can assuage this problem, however; and the overloads can even be templated on the argument types.
D必須使用與B選擇的參數相同的參數定義一個構造函數。可是,定義幾個create的重載能夠緩解這個問題。甚至能夠定義有關參數類型的模板形式重載。
If the requirements above are met, the design guarantees that post_initialize has been called for any fully constructed B-derived object. post_initialize doesn't need to be virtual; it can, however, invoke virtual functions freely.
若是知足上述要求,則設計將確保已爲全部徹底構造的B派生對象調用post_initialize。post_initialize不須要是虛擬的;可是,它能夠自由調用虛擬函數。
In summary, no post-construction technique is perfect. The worst techniques dodge the whole issue by simply asking the caller to invoke the post-constructor manually. Even the best require a different syntax for constructing objects (easy to check at compile time) and/or cooperation from derived class authors (impossible to check at compile time).
總之,沒有任何後建技術是完美的。最糟糕的技術是經過簡單地要求調用者手動調用後構造函數來規避整個問題。即便是最好的技術,也須要使用不一樣的語法來構造對象(在編譯時易於檢查)和/或派生類做者的合做(在編譯時沒法檢查)。
原文連接https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#discussion-use-a-factory-function-if-you-need-virtual-behavior-during-initialization
新書介紹
《實戰Python設計模式》是做者最近出版的新書,拜託多多關注!
本書利用Python 的標準GUI 工具包tkinter,經過可執行的示例對23 個設計模式逐個進行說明。這樣一方面可使讀者瞭解真實的軟件開發工做中每一個設計模式的運用場景和想要解決的問題;另外一方面經過對這些問題的解決過程進行說明,讓讀者明白在編寫代碼時如何判斷使用設計模式的利弊,併合理運用設計模式。
對設計模式感興趣並且但願隨學隨用的讀者經過本書能夠快速跨越從理解到運用的門檻;但願學習Python GUI 編程的讀者能夠將本書中的示例做爲設計和開發的參考;使用Python 語言進行圖像分析、數據處理工做的讀者能夠直接以本書中的示例爲基礎,迅速構建本身的系統架構。
以爲本文有幫助?請分享給更多人。
關注微信公衆號【面向對象思考】輕鬆學習每一天!
面向對象開發,面向對象思考!
本文分享自微信公衆號 - 面向對象思考(OOThinkingDalian)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。