假設定義了以下的基類和派生類:
class shape
{
public:
shape(int a){}; //基類構造
int length(){}; //基類中與子類重名函數
void display(){}; //基類中獨有
vitual int area(){}; //基類中虛函數
}
class rectangle:public shape
{
public:
rectangle(int);
int length();
int area();
}
1. 派生類的定義
rectangle::rectangle(int a):shape(int a){}; //派生類中使用基類構造函數
void rectangle::length()
{
shape::length(); //派生類中使用重名的父類函數
display(); //派生類中使用父類中獨有函數
}
2. 派生類實例的使用
rectangle rect();
shape theshape();
rect.length(); //調用rectangle::length()
rect.shape::length(); //顯式指定調用基類函數shape::length()
rect.display(); //display()被繼承,調用shape::display()
3. 對象的成員變量與成員函數的不同
成員變量在對象初始化之后,仍然可以進行賦值,更改。事實上,對對象的任何操作,也僅僅是對它的成員變量進行操作。
成員函數在對象初始化那一刻使確定下來,成員函數是根據該對象的類型進行綁定的,是編譯期已經確定的,靜態綁定。
各個實例的成員變量的地址是不同的,與實例的首地址有關。
各個實例的成員函數其實是共用的,因為只與他們的類型相關。
成員變量的地址(包括虛表變量)是按照各實例的首地址給的,各實例之間不相同。
成員函數的地址是按照實例的類型給的,各實例間相同。
4. 類型的轉換
要區分對象的類型轉換與對象指針的類型轉換:
- 實際上,指針的類型可以任意轉換,只是指針指向的地址發生變化而已。
- 對象的類型轉換,實際上是將右值的所有成員變量的值賦給左值。注意:右值的虛表變量不會賦值給左值!
- 就地轉換,實際上是隱含地調用了一次轉換后類型的構造函數,產生了一個臨時對象,將這個臨時對象作為左值。
rectangle rect;
shape shp;
shp = (shape)rect; //合法,將rect中與shp相同的那部分成員變量賦給shp
//等價于 shp.a = rect.a, shp.b = rect.b
// shp.vtable并沒有被rect.vtable覆蓋! 仍然是原來的。
rect = (rectangle)shp; //不合法,因為rect的一些成員變量,shp并沒有,所以賦值時要出錯
//rect.a = shp.a, rect.b = shp.b,rect.c = shp.?
rectangle* prect;
shape* pshp;
pshp = ▭ //pshp指向了rect的首地址
//pshp指向的成員變量是rect的成員變量
//(pshp可以指向一些shape類型沒有的成員變量)
// 但pshp綁定的成員函數還是shape類型的成員函數。
//pshp->vtable就是rect.vtable, 但pshp->length()調用的是 shape::length();
prect = &shp; //合法,但是沒有實際意義
5. 虛成員函數與普通成員函數
上面已提了,一個實例的普通成員函數在編譯期已經根據它的類型確實了。而虛函數不是這樣的,它的調用是根椐實例的成員變量虛表查到的。
rectangle rect;
shape shp;
shp.area(); //其實是shp.vtable->area();
rect.area(); //rect.vtable->area();
shape* pshp;
pshp = ?
pshp->area(); //實際上是pshp->vtable->area(),而pshp->vtable == rect.vtable,所以調用的是 rectangle::area();
pshp->length(); //length是普通的成員函數,根據pshp的類型確定,所以調用的是shape::length();
((shape)rect).area(); //就地轉換并調用,相當于下面的的兩行代碼
shape shp2 = rect;
shp2.area(); //shp2的成員變量被rect的成員變量覆蓋,但shp2.vtable并沒有變成rect.vtable,仍然調用shape::area();
6. 虛函數的用途:統一形式的調用
shape* pshps = [&triangle(), &rectangel()];
for(auto ipshp: pshps)
ipshp->area(); //分別調用了各個不同類型實例的area();