2011-09-21 122 views
1

我在搞C++中的繼承,想知道是否有人對它的功能有任何洞見。下面在C++中的繼承

#include <iostream> 
using namespace std; 

class AA { 
int aa; 
public: 
AA() {cout<<"AA born"<<endl;} 
~AA(){cout<<"AA killed"<<endl;} 
virtual void print(){ cout<<"I am AA"<<endl;} 
}; 

class BB : public AA{ 
int bb; 
public: 
BB() {cout<<"BB born"<<endl;} 
~BB() {cout<<"BB killed"<<endl;} 
void print() {cout<<"I am BB"<<endl;} 
}; 

class CC: public BB{ 
int cc; 
public: 
CC() {cout<<"CC born"<<endl;} 
~CC(){cout<<"CC killed"<<endl;} 
void print() {cout<<"I am CC"<<endl;} 
}; 


int main() 
{ 
AA a; 
BB b; 
CC c; 
a.print(); 
b.print(); 
c.print();  
return 0; 
} 

代碼,所以我明白,當你繼承你的東西繼承構造函數和析構函數。所以當我這樣做時,「BB b」打印出「AA生」。所以,問題我有

  1. 是AA的實例創建
  2. 如果有,是什麼叫我怎麼可以參考呢?
  3. 如果沒有,爲什麼被稱爲構造
+2

「是的創建實例」 - 我假定你的意思'AA'? – Flexo

+0

是的,我確實,謝謝我修復它。 – noob

+0

所以一個後續問題,如果AA有一個私有成員/函數,b.AA :: privatefunction()是否有效? – noob

回答

2

繼承實現了 「IS-A」 的關係。因此每個BB也是AA

您可以在許多方面看到這一點,最簡單的證明就是:

BB b; 
AA *aptr = &b; 

這裏您BB例如,通過一個指針,它只是將自己看作指向一個AA被指向b。如果BB沒有從AA繼承,那麼這將是不合法的。

有趣的是,當你撥打:

aptr->print(); 

它仍然打印「我是BB」,儘管你使用的指針AA *型。發生這種情況是因爲print()方法是virtual(即多態),並且您正在使用指針。 (這同樣也將與基準發生過,但類型必須是那些對這種行爲發生的一個)

1
  1. 是A的一個實例創建

排序的。 BB b;代碼將分配一個BB實例。該對象的一部分是AA

  1. 如果有,是什麼叫我怎麼可以參考呢?

假設BB b;變量聲明,你的一部分,OF- BBAA實例名爲b。如果你想調用AA具體方法由BB方法隱藏,如.print(),你需要調用它們像這樣:

BB b; 
b.AA::print(); 
+0

所以後續問題,如果AA有一個私有成員/函數,那麼b.AA :: privatefunction()是否有效? – noob

+0

你不能調用對象的私有函數,只有這個對象可以做到這一點 –

+0

@noob:該語法對私有函數在私有函數被允許的地方(例如在成員函數內)是有效的。請注意,在一個成員函數中,你可以調用它爲'this-> AA :: privatefunction()',或簡單地'AA :: privatefunction()'。 –

1

BBAA一個實例。你不需要訪問任何特別的東西,因爲你已經有了這種類型。

當從AABB繼承,這裏是它是如何構成的。

  1. 呼叫AA構造
  2. 初始化成員字段
  3. 呼叫BB構造

當破壞發生時,這種情況發生在反向。

  1. 調用析構函數BB
  2. 摧毀成員字段(具體以BB
  3. 調用析構函數AA

哦,你有AA內至少一個虛擬功能。這意味着您需要使AA的析構函數變爲虛擬(否則將會發生不好的事情)。

+0

析構函數只需虛擬就可以被虛擬刪除。在這種情況下,它根本就沒有關係,行爲已被很好地定義。 (也就是說,如果你使用'new' +'delete' *和*,你在一個指向基地的指針上調用'delete'而不是派生最多的指針,那麼這樣做就很糟糕。 – Flexo

+0

@awoodland:你如何保證這個對象不會被刪除?所以這個建議是好的,這應該是默認的實現,即使在這樣簡單的例子中 –

+0

你不能確定它不會,但是當前的措辭意味着這個代碼的行爲是不確定的,它不是。這是一條經驗法則,不是一個要求,從措詞上看不清楚。 – Flexo

0

從基類派生的類包含基類作爲子類,所以派生類的實例包含鹼子對象

的子對象首先構造。用於此目的的構造函數是您自己確定的:如果您什麼都不說,則使用默認的構造函數;否則,調用您在基礎初始值設定項列表中指定的構造函數。例如:

struct Base 
{ 
    Base() { }    // default constructor 
    Base(int, double) { } // some other constructor 
    char q; 
}; 

struct A : Base 
{ 
}; 

struct B : Base 
{ 
    B() : A(12, 1.5) { } 
}; 

兩個AB包含Base作爲一個子類。當你說A a;時,子對象的默認構造函數被調用。當你說B b時,子對象的其他構造函數被調用,因爲你這麼說。

因此,如果基類沒有可訪問的默認構造函數,則必須明確指定訪問的構造函數。

您可以通過限定名稱引用子對象的成員:a.Base::qb.Base::q。如果名稱是明確的,那就是一樣只是a.qb.q。但是,當您處理重寫或隱藏的成員函數(也可能是多個虛擬繼承)時,能夠明確指定子對象可能是有用的或甚至是必需的。

0

在面向對象的語言,也有創造,從另一個繼承的對象主要有兩種方式:串聯和委派。第一個是最流行的,第二個是基於原型的語言。連接意味着當B從A繼承時,對象中有兩部分:在對象的第一部分(長度等於A的大小)中,屬性(C++中的成員變量/字段)的活着。在另一部分中,存在B添加到對象的任何屬性。每個涉及類(A和B)的構造函數都被執行,因此所有的部分(所有屬性)都被正確初始化。

雖然對象是作爲一個單元來管理,就可以與類A的一個指針指向其在這種情況下,指針只允許您看到第一部分,該部分屬於A類(即它的屬性),而不是從B中的屬性,因爲指針只能看到從一開始就開始+尺寸類A的

說類A含有一個整數x:

class A { 
public: int x; 
}; 

和B包含一個整數Y:

class B: public A { 
public: int y; 
} obB; 

這意味着,B類的一個對象將公頃長度爲8個字節(對於32位),長度爲x加上y的長度,而A的長度僅爲4個字節,長度爲x。類A的指針objB

A * ptrA = &objB; 

只會看到x

cout << ptrA->x << endl; 
cout << ptrA->y << endl; // ERROR 

而一個指針,B:

B * ptrB = &objB; 

將能夠訪問這兩個中,x和y:

cout << ptrB->x << ',' << ptrB->y << endl; 

爲了完全理解這一點,你需要知道ptrB->y大致翻譯成*(((int *) ptrB) + 1),但這是另一個故事。

希望這會有所幫助。

0

繼承在C++是指在一類對象的繼承它實現了reusablity概念的另一個類的屬性的能力。這意味着如果需要,不需要創建新方法並僅覆蓋現有方法。節省了很多工作。