2012-03-12 56 views
2

例如,在這一段代碼,如果line [a]被註釋的那樣,輸出爲0。繼承:爲什麼繼承和提供的變量之間的行爲有所不同?

inh2.cpp

#include<iostream> 
using namespace std; 

class A { 
    public: 
     int x; 
     A() { x = 10; } 
}; 

class B : public A { 
    public: 
     int x; // <--------- [a] 
     B() { x = 0; } 
}; 

int main() { 
    A* ab = new B; 
    cout << ab->x << endl; 
} 

從GCC結果

$ g++ inh2.cpp 
$ ./a.out 
10 
$ 

我有兩個問題:

  1. 如何ab->x決心10在上述情況下?該對象的類型爲class B,因此應該值爲0
  2. 爲什麼評論Line [a]改變了代碼的行爲?我的推理是x反正會繼承,這應該導致相同的行爲。

我給上述,Q#1推理:

  • ab點的class B對象的內存位置。從某種意義上說,它是一個物理對象,所有具有它們值的變量都被分配了內存。

  • 變量x此對象內存儲值0

  • ab->x完成後,AB告訴我們對象的存儲位置,我們去看看它裏面找到x爲0,所以我們應該打印0

我在哪裏錯了這裏?

回答

3
  1. 是的,這是B型的,但你賦值爲一個指向A,因此它使用上的一個定義的x(如當我們用指針來處理A,我們不知道B即使存在,即使這是你分配的)。

  2. 當您在施工階段註釋掉線,A的構造函數首先被調用,然後B的構造函數,這臺x(在其基類)爲0,只有一個x在這一點上,而Bs構造函數被稱爲last。

+1

關於#1,在運行時,已知該對象的類型爲'B',所以它不應該解析爲0? – Lazer 2012-03-12 10:36:52

+0

@拉澤爾,誰知道?我們知道它*是* B,但是我們擁有的指針是一個A,這就是將要使用的。爲了測試這個,給B添加一個新成員'int y;'。現在做'ab-> y'。你會得到一個編譯器錯誤,因爲'y'不是'A'的成員。 – 2012-03-12 10:38:08

+0

'ab'指向'B類'對象的內存位置。從某種意義上說,它是一個物理對象,所有具有它們的值的變量都被分配了內存並且存在。該對象中的變量'x'存儲值'0'。當'ab-> x'完成時,ab告訴我們對象的內存位置,我們去查看它,發現x是'0'。所以我們應該打印'0'。不知道我在這裏錯過了什麼? [we = compiler] – Lazer 2012-03-12 10:41:33

1

製作一些小的修改:

#include <iostream> 

using namespace std; 

class A { 
public: 
    int x; 
    A() 
     :x(10) 
    { 
     std::cout << __FUNCTION__ << std::endl; 
     std::cout << x << std::endl; 
    } 
    virtual ~A() {} 
}; 

class B : public A { 
public: 
    int x; // <--------- [a] 
    B() 
     :A() 
     ,x(0) 
    { 
     std::cout << __FUNCTION__ << std::endl; 
     std::cout << x << std::endl; 
    } 
}; 

int main() { 
    A* ab = new B; 
    cout << "ab->x: " << ab->x << endl; 
    cout << "ab->A::x " << ab->A::x << endl; 

    B* b = dynamic_cast<B*>(ab); 
    cout << "b->x: " << b->x << endl; 
    cout << "b->A::x " << b->A::x << endl; 
    cout << "b->B::x " << b->B::x << endl; 
} 

這給了你:

A 
10 
B 
0 
ab->x: 10 
ab->A::x 10 
b->x: 0 
b->A::x 10 
b->B::x 0 

這表明:

  • ab->xA::x因爲ab是鍵入A*並且沒有這樣的東西作爲virtual變量。如果你想要多態性,你必須寫一個virtual int get_x() const方法。
  • B::x隱藏A::x。這是一個壞主意,應該避免。考慮爲成員變量使用更有意義的名稱,並確定在引入新變量之前是否可以重用基類的變量。
  • 通過鑄造到B*,您可以訪問B的會員以及A的會員。這應該是不言自明的。