1. 程式人生 > >C++語言筆記系列之十八——虛函數(1)

C++語言筆記系列之十八——虛函數(1)

自己 語言 數據類型 說明 出現 adium 重定義 angle rac

1.C++中的多態
(1)多態性:同一個函數的調用能夠進行不同的操作,函數重載是實現多態的一種手段。


(2)聯編:在編譯階段進行聯接。即是在編譯階段將一個函數的調用點和函數的定義點聯接起來。
A.靜態聯編:在編譯階段就完畢的函數聯編——函數重載。


B.動態聯編:在程序的執行階段由系統自己主動選擇詳細的函數——虛函數。


註:C++的多態主要指的就是動態聯編。
2.虛函數
(1)虛函數是在函數的定義時將其聲明為虛函數就可以。
(2)說明:virtual 數據類型 函數名(參數表) {函數體}
A.目的:當通過基類指針調用虛函數時,系統進行動態聯編。
B.在派生類中定義了和基類名稱同樣、參數個數同樣、參數類型同樣、返回值類型同樣的函數時。若基類中的同名成員函數是虛函數,派生類的同名成員函數將自己主動被虛化。


C.通過對象訪問虛函數時採用靜態聯編。

動態聯編僅僅可以通過指向對象的指針和對象的引用來完畢。
3.樣例
example 1

#include <iostream.h>

class Point(
{
private:
float x, y;
public:
void setpoint(float i, float j)
{
x = i; y = j;
}
float area() {return 0.0;}//等價於:float area()=0;
};
const float PI=3.1416;
class Circle:pulic Point
{
private:
float rad;
public:
void setrad(float r) {rad = r;}
float area() {return PI*rad*rad;}
};
int main()
{
Point p;
Circle c;
float a = p.area();
cout<<a<<endl;
c.setrad(2.5);
a = c.area();
cout<<a<<endl;
}
分析:
float a=p.area()調用的是Point類的area函數:p是基類Point的對象;對象調用函數時採用靜態聯編;Point類的對象調用area,area一定是Point類的area;a=c.area()調用的是Circle類的area函數,由於就近原則。


改動main函數:

int main()
{
Point *p;
Circle c;
float a;
c.setrad(2.5);
p = &c;
a = p->area();//該函數屬於靜態聯編
cout<<a<<endl;
}
程序輸出:
0.0
分析:雖然基類指針指向了派生類對象,但僅僅可以通過基類的指針引用派生類對象從基類繼承過去的成員。假設要想基類指針可以指向派生類的成員,那麽就要使用虛函數,從而實現動態聯編。


4.四種靜態聯編
(1)基類指針指向基類對象,採用靜態聯編調用基類成員。
(2)派生類的指針指向派生類的對象採用靜態聯編,調用派生類成員。若派生類沒有的成員。調用基類的該成員。


(3)基類指針指向派生類對象,採用靜態聯編,調用基類成員。


(4)對象引用成員一定是靜態聯編。基類對象引用基類成員,派生類對象引用派生類成員,若派生類沒有則能夠引用基類成員。
註:派生類指針不可指向基類,引用也不能夠。
5.動態聯編
(1)條件:基類的同名函數被聲明為虛函數。基類指針指向派生類對象。


(2)作用:使用動態聯編。使得一個基類指針能夠訪問多個派生類的成員函數,動態聯編僅僅能通過指針或引用來實現(前提是虛函數機制)。
example 2

#include <iostream.h>

class Point
{
public:
virtual double area() {return 0.0;}
};
class Rectangle:public Point
{
double length, width;
public:
Rectangle(double l, double w):length(l), width(w) {}
double area() {return length*width;}
};
class Circle:public Point
{
double radium;
public:
Circle(double r) {radium = r;}
double area() {return 3.1416*radium*radium;}
};
class Triangle:public Point
{
double x, y, z;
public:
Triangle(double a, double b, double c)
{x = a; y = b; z = c;}
double area() {return (x+y+z)*0.5;}
};
int main()
{
Point *p;
Rectangle r(5.0, 2.0);
Circle c(6.0);
Triangle t(3.0, 4.0, 5.0);
p = &r;
cout<<p->area()<<endl;
p = &c;
cout<<p->area()<<endl;
p = &t;
cout<<p->area()<<endl;
}
程序輸出:
10
113.098
6
註:基類的虛函數也能夠被調用。調用方法:加類的作用域(強制採用靜態聯編)。
演示樣例:
p = &r;
p->area();//調用Rectangle類的area函數
p->Point::area();//強制採用靜態聯編,調用Point類作用下的area函數
6.虛函數的訪問權限
(1)派生類中的虛函數不影響動態聯編,基類的虛函數是保證動態聯編的必要條件。
(2)一個類中的虛函數僅僅對派生類重定義的函數有影響,對它的基類成員無影響。


example 3

#include <iostream.h>

class A
{
public:
virtual void fun1()
{cout<<"fun1()...fun2()"<<endl; fun2();}
void fun2()
{cout<<"fun2()...fun3()"<<endl; fun3();}
virtual void fun3()
{cout<<"fun3()...fun4()"<<endl; fun4();}
virtual void fun4()
{cout<<"fun4()...fun5()"<<endl; fun5();}
void fun5() {cout<<"The end."<<endl;}
};
class B:public A
{
void fun3()
{cout<<"fun3...fun4"<<endl; fun4();}
void fun4()
{cout<<"fun4...fun5"<<endl; fun5();}
void fun5()
{cout<<"All done."<<endl;}
};
int main()
{
A *thing;
thing = new A;
thing->fun1();
thing = new B;
thing->fun1();
}
程序輸出:
fun1()...fun2()
fun2()...fun3()
fun3()...fun4()
fun4()...fun5()
The end.
fun1()...fun2()
fun2()...fun3()
fun3...fun4
fun4...fun5
All done.
7.若基類和派生類中的同名函數參數個數不同。參數類型不同,基類中的成員函數雖然是虛函數。但將丟失虛特性——採用靜態聯編。
基類和派生類中的同名成員函數,若出現基類中的同名成員函數非虛,派生類中同名成員函數是虛函數。那麽也將採用靜態聯編。
example 4

#include <iostream.h>

class Base
{
public:
virtual void fun1() {cout<<"Base fun1."<<endl;}
virtual void fun2() {cout<<"Base fun2."<<endl;}
void fun3() {cout<<"Base fun3."<<endl;}
void fun4() {cout<<"Base fun4."<<endl;}
};
class Device:public Base
{
public:
virtual void fun1()
{cout<<"Device fun1."<<endl;}
virtual void fun2()
{cout<<"Device fun2."<<endl;}
virtual void fun3()
{cout<<"Device fun3."<<endl;}
virtual void fun4()
{cout<<"Device fun4."<<endl;}
};
int main()
{
Base *pb;
Device d;
pb = &d;
pb->fun1();
pb->fun2();
pb->fun3();
pb->fun4();
}
程序輸出:
Device fun1.
Device fun2.
Base fun3.
Base fun4.

C++語言筆記系列之十八——虛函數(1)