复习笔记(五)——C++多态和虚函数
靜態綁定與動態綁定
靜態綁定:
-編譯時就能確定一條函數調用語句要調用的函數
-在程序編譯時多態性體現在函數和運算符的重載上
動態綁定:
-運行時才能確定函數調用語句調用的函數
-程序運行時的多態性通過繼承和虛函數來體現
多態
什么是多態?
顧名思義就是同一個事物在不同場景下的多種形態。
多態性的概念
多態性是面向對象程序設計的重要特征之一。
多態性概念:具有繼承關系的類,其對象對同一個函數調用可以作出不同的響應
-同一個函數調用——同一條函數調用語句
-不同的響應——執行不同的函數
多態性的優點
①多態性有助于更好地對程序進行抽象
-控制模塊能專注于一般性問題的處理
-具體的操作交給具體的對象去做
②多態性有助于提高程序的可擴展性
-可以把控制模塊與被操作的對象分開
-可以添加已定義類的新對象,并能管理該對象
-可以添加新類(已有類的派生類)的新對象,并能管理該對象
那什么時候會用到多態呢?
當我們需要隱藏子類對象的多余屬性和方法時,所以利用父類指針指向子類對象時,就可以完美避免誤操作調用了子類對象的屬性和方法。
靜態多態
我們以前說過的函數重載就是一個簡單的靜態多態
示例:
int Add(int left, int right) {return left + right; } double Add(double left, int right) {return left + right; }int main() {Add(10, 20);Add(10.0,20); return 0; }虛函數(動態多態)
虛函數的概念
概念:在基類中冠以關鍵字 virtual 的成員函數
虛函數的定義:virtual 函數類型 函數名稱(參數列表);
-注意:如果一個函數在基類中被聲明為虛函數,則他在所有派生類中都是虛函數(包括重定義函數)
虛函數的作用:用指針/引用調用時會發生動態的綁定。
注意:①只有通過基類指針或引用調用虛函數才能引發動態綁定
②聲明時要加virtual,定義時不用加。
實例
#include<iostream> using namespace std;class A { public:A(){cout<<"A::construct"<<endl;}virtual void print(){cout<<"This is A"<<endl;} };class B : public A { public:B(){cout<<"B::construct"<<endl;}void print(){cout<<"This is B"<<endl;} };int main() {A a;a.print();B b;b.print(); /*結果: A::construct This is A A::construct B::construct This is B*///B b; //A *a; //調用B中的print //a = &b; //a->print(); /*結果: A::construct B::construct This is B*///B b; //A &a = b; //調用B中的print //a.print(); /*結果: A::construct B::construct This is B*///B b; //A a; //虛函數不會調用B中的print //a = b; //a.print(); /*結果: A::construct B::construct A::construct This is A*/return 0; }虛函數的注意事項
- 在類體系中訪問一個虛函數時,應使用指向基類類型的指針或基類類型的引用,以滿足運行時多態性的要求。當然也可以像調用普通成員函數那樣利用對象名來調用一個函數。
- 在派生類中重新定義虛函數時,必須保證該函數的值和參數與基類中的說明完全一致,否則就屬于重載。
- 若在派生類中沒有重新定義虛函數,則該類的對象將使用其基類中的虛函數代碼。
- 虛函數必須是類的一個成員函數,不能是友元,但它可以是另一個類的友元。虛函數不得是一個靜態成員。
虛函數和重載函數的區別(重點)
- 成員函數被重載需要在相同范圍(同一個類中),而虛函數要求在不同的范圍(一個在派生類,一個在基類)
- 重載函數要求函數有相同函數名稱,并有不同的參數序列;而虛函數則要求函數名、返回值類型和參數序列完全相同
- 重載函數可以是成員函數或友員函數,而虛函數只能是成員函數
- 重載函數的調用是以所傳遞參數序列的差別作為調用不同函數的依據;虛函數是根據對象的不同去調用不同類的虛函數
- 虛函數在運行時表現出多態功能,這是C++的精髓;而重載函數則在編譯時表現出多態性
虛析構函數
析構函數可以聲明為虛函數:
-程序會根據基類指針指向的對象的類型確定要調用的析構函數
動態對象的創建和撤銷
動態對象的創建:
new ClassName(…);
ClassName指明了要調用的構造函數
動態對象的撤銷:
delete 基類指針;
虛析構函數作用
虛析構函數的作用就是:當用一個基類的指針刪除一個派生類的對象時,派生類的析構函數會被調用
下面用一個簡單的例子來說明吧
#include<iostream> using namespace std;class ClxBase { public :ClxBase(){};virtual ~ ClxBase(){};virtual void DoSomething(){cout<<" Do something in class ClxBase! "<<endl;}; };class ClxDerived:public ClxBase { public :ClxDerived(){};~ ClxDerived(){cout<<" Output from the destructor of class ClxDerived! "<<endl;};void DoSomething(){cout<<" Do something in class ClxDerived!"<<endl;}; };int main() {ClxBase * pTest = new ClxDerived;pTest -> DoSomething();delete pTest;return 0; }執行結果是這樣的:
Do something in class ClxDerived! Output from the destructor of class ClxDerived!但是,如果把類ClxBase析構函數前的virtual去掉,那輸出結果就是下面的樣子了:
Do something in class ClxDerived!也就是說,類ClxDerived的析構函數根本沒有被調用!一般情況下類的析構函數里面都是釋放內存資源,而析構函數不被調用的話就會造成內存泄漏。當然,這并不意味著要把所有類的析構函數都寫成虛函數。
注意事項
①基類的析構函數為虛函數,所有派生類的析構函數都是虛函數
②構造函數不得是虛函數
③如果要操作具有繼承關系的類的動態對象,最好使用虛析構函數。特別是在析構函數需要完成一些有意義的操作——比如釋放內存
純虛函數
實現多態性的前提: ①需要有共同的基類 ②需要在基類中定義共同的接口 ③接口要定義為虛函數
如果基類的接口沒辦法實現怎么辦? 解決方法:①不實現這些接口:純虛函數 ②包含純虛函數的類:抽象基類
純虛函數定義
在基類中不能給出有意義的虛函數定義,這時可以把它說明成純虛函數,把它的定義留給派生類來做。(簡單的說:①純虛函數是被標明為不具體實現的虛函數②純虛函數的實現留給該基類的派生類去做。)
virtual 類型 函數名(參數名)=0;抽象類
如果一個類中至少有一個純虛函數,那么這個類被成為抽象類(abstract class)。
抽象類存在的意義是作為其它類的基類,也叫抽象基類。
主要作用:取若干類的共同行為,形成更清晰的概念層次。使用抽象類符合程序設計中的單選原則(single choice principle)。
注意:
①抽象類不能用于直接創建對象實例,可以聲明抽象類的指針和引用
②可使用指向抽象類的指針支持運行時多態性
③派生類中必須重載基類中的純虛函數,否則它仍將被看作一個抽象類
實例
#include<iostream>#define PI 3.14using namespace std;class Shape { public:virtual double area() const = 0;virtual void show() const = 0; };class Circle :public Shape { public:Circle(double=0.0,double=0.0,double=1.0);double area() const;void show() const; private:double x,y;double r; }; Circle::Circle(double a, double b, double c) {x = a;y = b;r = c; } double Circle::area() const {return PI*r*r; } void Circle::show() const {cout<<"I am a Circle: "; } class Rectangle :public Shape { public:Rectangle(double = 1.0, double = 1.0);double area() const;void show() const; private:double length;double width; }; Rectangle::Rectangle(double a, double b) {length = a;width = b; } double Rectangle::area() const {return length*width; } void Rectangle::show() const {cout<<" I am a Rectangle: "; }void callArea(Shape &obj) {obj.show();cout<<"area = "<<obj.area()<<endl; }int main() {Circle cir(0.0, 0.0, 2.5);Rectangle rec(2.4, 5.3);callArea(cir);callArea(rec);return 0; }執行結果:
I am a Circle: area = 19.625 I am a Rectangle: area = 12.72總結
以上是生活随笔為你收集整理的复习笔记(五)——C++多态和虚函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python学习笔记(七)——类基础
- 下一篇: 复习笔记(六)——C++运算符重载(难点