為什麼不要在建構函式中調用虛函數__函數

來源:互聯網
上載者:User

先看一段在建構函式中直接調用虛函數的代碼:

 1 #include <iostream> 2  3 class Base 4 { 5 public: 6     Base() { Foo(); }   ///< 列印 1 7  8     virtual void Foo() 9     {10         std::cout << 1 << std::endl;11     }12 };13 14 class Derive : public Base15 {16 public:17     Derive() : Base(), m_pData(new int(2)) {}18     ~Derive() { delete m_pData; }19 20     virtual void Foo()21     {22         std::cout << *m_pData << std::endl;23     }24 private:25     int* m_pData;26 };27 28 int main()29 {30     Base* p = new Derive();31     delete p;32     return 0;33 }

  這裡的結果將列印:1。

  這表明第6行執行的的是Base::Foo()而不是Derive::Foo(),也就是說:虛函數在建構函式中“不起作用”。為什麼。

  當執行個體化一個衍生類別對象時,首先進行基類部分的構造,然後再進行衍生類別部分的構造。即建立Derive對象時,會先調用Base的建構函式,再調用Derive的建構函式。

  當在構造基類部分時,衍生類別還沒被完全建立,從某種意義上講此時它只是個基類對象。即當Base::Base()執行時Derive對象還沒被完全建立,此時它被當成一個Base對象,而不是Derive對象,因此Foo綁定的是Base的Foo。

  C++之所以這樣設計是為了減少錯誤和Bug的出現。假設在建構函式中虛函數仍然“生效”,即Base::Base()中的Foo();所調用的是Derive::Foo()。當Base::Base()被調用時衍生類別中的資料m_pData還未被正確初始化,這時執行Derive::Foo()將導致程式對一個未初始化的地址解引用,得到的結果是不可預料的,甚至是程式崩潰(訪問非法記憶體)。

  總結來說:基類部分在衍生類別部分之前被構造,當基類建構函式執行時衍生類別中的資料成員還沒被初始化。如果基類建構函式中的虛函數調用被解析成調用衍生類別的虛函數,而衍生類別的虛函數中又訪問到未初始化的衍生類別資料,將導致程式出現一些未定義行為和bug。

  對於這一點,一般編譯器會給予一定的支援。如果將基類中的Foo聲明成純虛函數時(看下面代碼),編譯器可能會:在編譯時間給出警告、連結時給出符號未解析錯誤(unresolved external symbol)。如果能產生可執行檔,運行時一定出錯。因為Base::Base()中的Foo總是調用Base::Foo,而此時Base::Foo只聲明沒定義。大部分編譯器在連結時就能識別出來。

 1 #include <iostream> 2  3 class Base 4 { 5 public: 6     Base() { Foo(); }   ///< 可能的結果:編譯警告、連結出錯、執行階段錯誤 7  8     virtual void Foo() = 0; 9 };10 11 class Derive : public Base12 {13 public:14     Derive() : Base(), m_pData(new int(2)) {}15     ~Derive() { delete m_pData; }16 17     virtual void Foo()18     {19         std::cout << *m_pData << std::endl;20     }21 private:22     int* m_pData;23 };24 25 int main()26 {27     Base* p = new Derive();28     delete p;29     return 0;30 }

  如果編譯器都能夠在編譯或連結時識別出這種錯誤調用,那麼我們犯錯的機會將大大減少。只是有一些比較不直觀的情況(看下面代碼),編譯器是無法判斷出來的。這種情況下它可以產生可執行檔,但是當程式運行時會出錯。

 1 #include <iostream> 2  3 class Base 4 { 5 public: 6     Base() { Subtle(); }   ///< 執行階段錯誤(pure virtual function call) 7  8     virtual void Foo() = 0; 9     void Subtle() { Foo(); }10 };11 12 class Derive : public Base13 {14 public:15     Derive() : Base(), m_pData(new int(2)) {}16     ~Derive() { delete m_pData; }17 18     virtual void Foo()19     {20         std::cout << *m_pData << std::endl;21     }22 private:23     int* m_pData;24 };25 26 int main()27 {

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.