2016-07-15 74 views
1
#include <iostream> 
#include <string> 
using namespace std; 

class Base { 
public: 
Base(const string& s): str(s) {cout<<"Base::ctor\n";} 
Base(const Base& b): str(b.str) {cout<<"Base::copy ctor\n";} 
virtual ~Base() {cout<<"Base::dtor\n";} 
void f1() {cout<<"Base::f1()\n"; f2();} //2 orders 
virtual void f2() {cout<<"Base::f2()\n";} 

private: 
string str; 
}; 

class Derived : public Base { 
public: 
Derived(const string& s): Base(s) 
{cout<<"Derived::ctor\n";} 
Derived(const Derived& d): Base(d) 
{cout<<"Derived::copy ctor\n";} 
~Derived() {cout<<"Derived::dtor\n";} 
virtual void f1() {cout<<"Derived::f1()\n"; f2();} 
void f2() {cout<<"Derived::f2()\n"; f1();} //jumps from here to Leaf's f1() 
}; 

class Leaf : public Derived { 
public: 
Leaf(const string& s): Derived(s) 
{cout<<"Leaf::ctor\n";} 
Leaf(const Leaf& dd): Derived(dd) 
{cout<<"Leaf::copy ctor\n";} 
~Leaf() {cout<<"Leaf::dtor\n";} 
void f1() {cout<<"Leaf::f1()\n"; f3();} 
void f3() {cout<<"Leaf::f3()\n";} 
}; 


int main() { 
Leaf * p = new Leaf("Hello"); 
Base * p2 = new Leaf(*p); 

p2->f1(); 

delete p2; 
delete p; 
return 0; 
} 

你好,當通過Derived :: f2()調用f1()時,誰的函數被調用?

這個問題是考試的措辭之一,但它是我很難找到來形容它,並期待它在網上的正確途徑。

在行:

p2->f1(); 

輸出爲:

Base::f1() 
Derived::f2() 
Leaf::f1() 
Leaf::f3() 
中的F2

()有一個爲F1()的調用。誰會被稱爲? f1()的類型爲Base或f1()的Leaf? 從我所教的內容來看,編譯器總是在類型左邊尋找函數。 (Base * p2 = new Leaf(* p))但是在這裏我可以看到它轉到class Leaf的f1()。 我可以看到它是葉的,但不明白爲什麼...

感謝您的幫手!

+4

從您的實際輸出中是不是明顯的答案? –

+0

這個答案是一個事實。正如我在我的文章中所描述的,我不明白爲什麼它調用Leaf的f1()而不是Base的f1()... – EilonBom

+0

你對vtable的問題是什麼? – curiousguy

回答

2

要快速回答您的問題:在Derived :: f2()中調用Derived :: f1()。

要理解爲什麼它的派生:: F1()這就是所謂的,你可能需要的「C++名藏匿在繼承」的知識,您可以參考像一些網上的文章:

您還需要「不合格的名稱查找」,您可以參考「成員函數定義」一節中該網頁的知識:Unqualified name lookup

綜上所述,主要要點是:

  • 在你的代碼的情況下,派生:: F1()隱藏基:: F1(),這意味着基地:: F1()是不可見給Derived的成員。
  • 對f1()的調用是一個非限定名稱查找案例。
  • 名稱通常從最內部的範圍向外部查找。當在Derived :: f2()中調用f1()時,編譯器首先在最內部的範圍內查找,這是Derived :: f2()主體本身;然後是整個類的Derived作用域。因爲可以在Derived的範圍中找到f1(),所以它就成爲被調用的範圍。
  • 您可能會認爲Base :: f1()因爲繼承而與Derived :: f1()坐在同一級別,然後想知道爲什麼不調用Base :: f1()。回憶名字隱藏。

呼叫的過程應該如下:

  1. 在main(),p2->f1();被執行。
  2. 因爲p2是指向Base的指針,所以在Base的方法列表中搜索「f1」的名稱。
  3. 請注意,Base :: f1()是不是虛擬的,所以調用Base :: f1()(「Base :: f1()」)。是的,在Derived中將f1()聲明爲虛擬的,但這不會影響Base的虛擬表。
  4. Base :: f1()調用f2,它是Base的虛擬方法。由於f2僅在Derived中被覆蓋,Derived :: f2()是實際調用的那個(「Derived :: f2()」)。
  5. Derived :: f2()調用實際上是Derived :: f1()的f1()。因爲Derived :: f1()在Leaf中聲明爲虛擬並被重寫,所以最終調用它爲Leaf :: f1()(「Leaf :: f1()」)。
  6. Leaf :: f1()調用f3(),它是Leaf :: f3()。 f3()是一種只有Leaf纔有的方法,因此它只是被稱爲(「Leaf :: f3()」)。
+0

謝謝yaobin!非常有幫助...所以我的結論是,如果一個方法在類中調用(例如派生),它會調用相同的類方法 - 意味着Derived :: f2()(然後如果有繼承等... )? – EilonBom

+0

@EilonBom我編輯了我的答案,並添加了一些可以查看的參考資料。希望他們能夠讓你更好地理解幕後發生的事情。這應該在帖子和評論中解決您的問題。 – yaobin

+0

「f1的地址被搜索」< - 沒有地址搜索正在進行,我們正在進行名稱查找。 – Barry