2010-07-19 72 views
0

我在一兩個小時前問過一個類似的問題,但這與我根本不同。在此之後,我應該很好。關於繼承/鑄造的一些最終問題

class base 
{ 
private: 
    string tame; 
public: 
    void kaz(){} 
    virtual ~base() {} 
    void print() const 
    { 
     cout << tame << endl; 
    } 
}; 

class derived: public base 
{ 
private: 
    string taok; 
public: 
    std::string name_; 
    explicit derived(const std::string& n) : name_(n) {} 
    derived(){} 
    void blah(){taok = "ok";} 
    void print() const 
    { 
     std::cout << "derived: " << name_ << std::endl; 
    } 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    base b; 
    derived d; 

    base * c = &b; 
    derived * e = (derived *)&b; 

    e->kaz(); 
    system("pause"); 


    return 0; 
} 

我知道在這個例子中向下轉換是不好的做法,但我只是用它作爲一個例子。所以當我現在從一個派生指針指向一個基礎對象時,我不明白爲什麼我仍然能夠執行僅屬於基類的某些操作。

例如,基類的接口有一個Kaz()方法,但派生方法沒有。當我沮喪時,即使Kaz()不是派生類的接口的一部分,爲什麼編譯器不會因爲這樣做而大叫?

  1. 爲什麼編譯器在使用派生指針時不會抱怨使用基類成員?

  2. 爲什麼只有當我從方法內部訪問基類接口中的成員時,編譯器纔會對我大聲叫嚷?

例如:

我不能做到這一點:

e->print() //Program crashes 

但我可以這樣做:如果你仔細看看,沒有

e->tame = "Blah"; 
cout << e->tame << endl; 

回答

1

派生類繼承基類的所有成員,因此kaz()也存在於derived對象。如果在derived對象上調用kaz(),則只需調用從base繼承的方法。如果從一個方法中訪問繼承的成員或直接無關緊要。

e的問題在於它確實指向base對象,而不是derived。有了演員e = (derived *)&b,你告訴編譯器「我知道它看起來不像,但這真的是一個derived *,相信我!」。編譯器相信你,因爲你是主人。但你撒謊和&b實際上是而不是 a derived*。因此,當編譯器試圖調用derived::print()時會發生可怕的事情,在這種情況下會導致程序崩潰。

當你直接訪問e->tame,也是可怕的事情可能發生(編譯器仍然把e作爲derived*而僅是一個base*)。在這種情況下,無論如何,它恰巧打印出期望值。

-1

派生的構造函數會初始化基本名稱,而基礎構造函數(這是唯一使用的構造函數)也不會。

所以當你做e->print()你想打印的值是未定義的。

當您手動設置它時,其不再未定義。因此,要e->print()通話將工作得很好

你可以試試這個

在基類中定義接收名稱

base(std::string name):tame(name){} 
+0

沒關係,下面回答。 – Ilya 2010-07-19 22:27:50

+0

派生實例可以訪問所有的基礎成員,因爲您可以繼承並且不重新定義任何。 – Tom 2010-07-19 22:30:35

+0

我downvoted,因爲std :: string的空構造函數將始終初始化爲空字符串。它不是一個未初始化的值,就像他使用過一種基本類型一樣。 – Puppy 2010-07-19 22:33:53

2

首先非默認的構造函數,你應該使用dynamic_cast<derived*>(&b)不是C的風格的演員。

然後,如果您想覆蓋子類中的方法,則應聲明print()方法爲virtual

e->tame = "Blah";應該會導致編譯器發出錯誤(如果不在類方法內部使用,因爲它被聲明爲private)。

最後可以在派生對象上調用kaz()方法,因爲子類包含基類中的所有方法以及派生類中定義的方法。

+0

@Martin York:因爲'b'被聲明爲'base b;',所以需要強制轉換。否則編譯器會停止並顯示錯誤。 – MKroehnert 2010-07-20 06:52:34

0

沒關係,有人在另一個線程中加入了他們的迴應,而這個線程現在有很多意義。謝謝

0

從編譯器的角度來看,e是一個「派生」的對象指針。

從運行時的角度來看,e指向一個「基本」對象,所以e-> name_指向一個隨機地址。儘管你投了它,但它並沒有改變它是被分配的「基礎」對象的事實。