多态的摸索之路( 甲 )
C++ 中的多態(tài)是極為復雜、難纏、龐大的,就像深海里的鯊魚,兇猛而強大。那么到底如何才能游刃有余地駕于好這頭潛伏在深海的遠古猛獸呢?!別著急,咱們慢慢來,一點一點地摸索清它的性格和脾氣,再控制就容易啦。
例甲:
#include<iostream>
using namespace std;
class TradesPerson {
public:
?????? virtual void sayHi() { cout << "Just Hi, and nothing." << endl; }
};
class Tinker : public TradesPerson {
public:
?????? virtual void sayHi() { cout << "Hi, I'm a Tinker." << endl; }
};
class Tailor : public TradesPerson {
public:
?????? virtual void sayHi() { cout << "Hi, I'm a Tailor." << endl; }
};
?
int main() {
?????? TradesPerson* p;
?????? int which;
?????? do{
????????????? cout << "1 = TradesPerson, 2 = Tinker, 3 = Tailor, 4 = quit; /n? ";
????????????? _input: cin >> which;
?????? }while( which < 1 || which >4 );
?????? switch( which ) {
????????????? case 1: p = new TradesPerson; break;
????????????? case 2: p = new Tinker; break;
????????????? case 3: p = new Tailor; break;
????????????? case 4: exit( 0 );
?????? }
?????? p->sayHi();
?????? goto _input;
?????? delete p;
?????? return 0;
}
?
模型一:
class TradesPerson {
public:
?????? virtual void sayHi() { cout << "Just Hi, and nothing." << endl; }
};
class Tinker : public TradesPerson {
public:
?????? virtual void sayHi() { cout << "Hi, I'm a Tinker." << endl; }
};
class Tailor : public TradesPerson {
public:
?????? virtual void sayHi() { cout << "Hi, I'm a Tailor." << endl; }
};
?
模型二:
class TradesPerson {
public:
?????? virtual void sayHi() { cout << "Just Hi, and nothing." << endl; }
};
class Tinker : public TradesPerson {
public:
?????? ?void sayHi() { cout << "Hi, I'm a Tinker." << endl; }
};
class Tailor : public TradesPerson {
public:
?????? ?void sayHi() { cout << "Hi, I'm a Tailor." << endl; }
};
?
模型三:
class TradesPerson {
public:
?????? void sayHi() { cout << "Just Hi, and nothing." << endl; }
};
class Tinker : public TradesPerson {
public:
?????? ?void sayHi() { cout << "Hi, I'm a Tinker." << endl; }
};
class Tailor : public TradesPerson {
public:
?????? ?void sayHi() { cout << "Hi, I'm a Tailor." << endl; }
};
?
模型四:
class TradesPerson {
public:
?????? void sayHi() { cout << "Just Hi, and nothing." << endl; }
};
class Tinker : public TradesPerson {
public:
?????? virtual void sayHi() { cout << "Hi, I'm a Tinker." << endl; }
};
class Tailor : public TradesPerson {
public:
?????? virtual void sayHi() { cout << "Hi, I'm a Tailor." << endl; }
};
?
實驗結(jié)果:模型一和模二相等,模三和模四相等。那么也就是說,當聲明了基類的一個成員函數(shù)為虛后,那么如果該函數(shù)在派生類中再次出現(xiàn)并得到新定義,它將自動成為虛函數(shù)!可如果在基類沒有聲明為虛,那么到派生類再怎么聲明是虛的都變得毫無意義了——根本起不到多態(tài)的神奇妙用。
說到多態(tài),咱們在此再重復一下多態(tài)的概念:多態(tài)性也稱后約束或動態(tài)約束,它常用虛函數(shù)來實現(xiàn)。在C++中,多態(tài)是指C++的代碼可以根據(jù)運行情況的不同而執(zhí)行不同的操作。C++的多態(tài)性就是為同一個函數(shù)和運算符定義幾個版本。C++支持兩種多態(tài)性,編譯時的多態(tài)性和運行時的多態(tài)性。編譯時的多態(tài)性通過使用重載函數(shù)獲得,運行時的多態(tài)性通過使用繼承和虛函數(shù)獲得。要獲得多態(tài)性的對象,必須建立一個類等級,然后在派生類中重定義基類函數(shù),該函數(shù)可以被定義為重載函數(shù)或虛函數(shù),以獲得編譯時的多態(tài)性對象或運行時的多態(tài)性對象。
?
?
一次非常成功而快樂的多態(tài)實驗!趕快來先睹為快吧!
例甲:
class A {
public:
?????? void disp() { cout << "This is A speaking!/n"; }
};
class B : public A {
public:
?????? void disp() { cout << "This is B speaking!/n"; }
};
?
int main() {
?????? A* pa = new B;
?????? pa->disp(); ???????? //輸出為This is A speaking!
?????? delete pa;
?????? return 0;
}
如果為了讓其輸出"This is B speaking!/n"的話,你可能會把那個關(guān)鍵句改成pa->B::disp();,可實驗證明,這么做編譯為報錯的。而且顯示三個錯誤,什么“'B' : is not a member of 'A'”、“ 'B' : is not a class or namespace name”等一派胡言亂語!那好,既然讓其輸出B speaking已經(jīng)不可能了,接下來就virtual伺候吧!
例乙:
class A {
public:
?????? virtual void disp() { cout << "This is A speaking!/n"; }
};
class B : public A {
public:
?????? void disp() { cout << "This is B speaking!/n"; }
};
?
int main() {
?????? A* pa = new B;
?????? pa->disp(); ???????? //輸出為This is B speaking!
?????? delete pa;
?????? return 0;
}
很明顯,無非多了個關(guān)鍵字兒virtual,世界競?cè)绱舜笞?#xff01;指針pa調(diào)用了B類里的成員函數(shù),這也是應該的、應得的、正常的!因為它pa是B類給的它初始化!雖然A類是pa的親生母親,但是,是B類一把屎一把尿養(yǎng)活了pa!所以當pa長大成人、能做事能掙錢時,它理應贍養(yǎng)孝順B類!!這沒什么好商量的!!
如果想讓其輸出This is A speaking!,也可以,直接修改關(guān)鍵句為: pa->A::disp(); 就行了!
對比例甲與例乙,我們看到,當沒有用虛函數(shù)時,只能輸出A speaking,不能企圖通過用域解析符::來改變函數(shù)調(diào)用輸出B speaking;但使用了virtual后,代碼好像自己長眼睛了,知道程序員心里真正想調(diào)用的是誰并把程序員的心聲正確付諸于實踐,而且函數(shù)調(diào)用也變得來去自如,想調(diào)誰調(diào)誰!(欲調(diào)其他類的同名成員函數(shù),只需加個域解析符即可!)。
不過也別高興的太早,這兩道例子看起來簡單無比,可我硬是花了幾個星期地功夫才摸索出來。難的、使我在大門外急得直跺腳就是進不來的東西是:你能否正確實驗并探究出虛函數(shù)的精要!也就是說,你很容易做出錯誤的實驗!廢話少說,來看看一條“不該地實驗,不該地愛……”
例甲:
int main() {
?????? B* pb = new A;
?????? pb->disp();
?????? delete pb;
?????? return 0;
}
用派生類B創(chuàng)建指針對象!用基類A給它初始化!結(jié)果一編譯呢——錯!“cannot convert from 'class A *' to 'class B *'”。所以呢,我估且得出一個結(jié)論:如果不想跟編譯器過不去,就不能用派生類創(chuàng)建對象然后基類初始化。只能是 A* pa = new B; ,謹記謹記!
?
?
?
以下三個例子中,主函均為:
int main() {
?????? A* pa = new B;
?????? pa->disp();
?????? delete pa;
?????? return 0;
}
因而全部省略。
?
例甲:
class A {
public:
?????? A() { cout << "A constructed,yeah!/n"; }?
?????? virtual void disp() { cout << "This is A speaking!/n"; }
};
?
class B : public A {
public:
?????? B() { cout << "B constructed,yeah!/n"; }?
?????? virtual void disp() { cout << "This is B speaking!/n"; }
};
輸出:
A constructed,yeah!
B constructed,yeah!
This is B speaking!
?
例乙:
class A {
public:
?????? ~A() { cout << "A destructed/n"; }
?????? virtual void disp() { cout << "This is A speaking!/n"; }
};
?
class B : public A {
public:
?????? ~B() { cout << "B destructed/n"; }
?????? virtual void disp() { cout << "This is B speaking!/n"; }
};
輸出:
This is B speaking!
A destructed
?
例丙:
class A {
public:
?????? A() { cout << "A constructed,yeah!/n"; }
?????? ~A() { cout << "A destructed/n"; }
?????? virtual void disp() { cout << "This is A speaking!/n"; }
};
class B : public A {
public:
?????? B() { cout << "B constructed,yeah!/n"; }
?????? ~B() { cout << "B destructed/n"; }
?????? virtual void disp() { cout << "This is B speaking!/n"; }
};
輸出:
A constructed,yeah!
B constructed,yeah!
This is B speaking!
A destructed
?
來個簡單明晰點的:
class A {
public:
?????? ~A() { cout << "A destructed/n"; }
};
class B : public A {
public:
?????? ~B() { cout << "B destructed/n"; }
};
?
?
int main() {
?????? A* pa = new B;
?????? delete pa;????? // 輸出 A destructed
?????? return 0;
}
接下來我不禁要問了,為什么析構(gòu)函數(shù)不像構(gòu)造函數(shù)那樣兩個類的統(tǒng)統(tǒng)都調(diào)用呢?
答案明日揭曉!!
?
?
?
總結(jié)
以上是生活随笔為你收集整理的多态的摸索之路( 甲 )的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 让工程师沉思的68个经典小故事
- 下一篇: 我的2018新年计划