類似LINUX內核驅動子系統,如下面的示意代碼:
if(fb->open)
fb->open();
當我們從更底層對fb->open()進行封裝了的時候,對應的系統調用不再是系統默認的,而是調用到我們更底層的fb->open.
這個C++裏面的多態思維有點相仿.如果基類和其派生類都定義了相同(部分相同)的一個方法的話,我們可以選擇調用基類或其派生類的一個方法.
C++的多態主要分兩個方面:編譯時和運行時,兩者分別對應着重載和覆蓋.
重載:
載函數必須具有不同的參數個數,或不同的參數類型.僅返回值不同時,不能定義爲重載函數.
下面給出一個示例.
源碼:
#include <iostream>
class simpleClass
{
public:
void func(char);
void func(int);
void func(int,int);
};
void simpleClass::func(char a)
{
std::cout << "funcChar" << std::endl;
}
void simpleClass::func(int i)
{
std::cout << "funcInt" << std::endl;
}
void simpleClass::func(int i,int j)
{
std::cout << "funcIntInt" << std::endl;
}
int main(void)
{
char ch = 7;
int i = 77;
int j = 777;
simpleClass *cls1 = NULL;
cls1 = new simpleClass;
cls1->func(ch);
cls1->func(i);
cls1->func(i,j);
delete cls1;
cls1 = NULL;
return 0;
}
編譯運行:
root@se7en-LIFEBOOK-LH531:~/learn/Cpp_Program# g++ reload.cpp -o reload
root@se7en-LIFEBOOK-LH531:~/learn/Cpp_Program# ./reload
funcChar
funcInt
funcIntInt
可見,函數的重載必須是實參的類型或數量不同.
覆蓋:
覆蓋是指派生類的某函數和基類完全一致,注意這裏是完全一致,而不是像上面的重載一樣要有所區別.重載是編譯時確定下來的,而覆蓋則是在運行時選擇的.此時,該方法需要
加關鍵字"virtual"修飾.至於選擇調用是基類的函數還是其派生類的函數的話,由實際的實例對象決定.
爲了加深印象,下面給出非多態的一個示例:
源碼:
#include <iostream>
using namespace std;
class baseClass
{
private:
unsigned int age;
bool sex;
public:
baseClass(unsigned int a,bool s)
{
age = a;
sex = s;
}
~baseClass()
{
std::cout << "~Base Class"<< std::endl;
}
unsigned int baseGetAge(void);
bool baseGetSex(void);
void display(void);
};
unsigned int baseClass::baseGetAge(void)
{
return age;
}
bool baseClass::baseGetSex(void)
{
return sex;
}
void baseClass::display(void)
{
std::cout << "base Class Display" << std::endl;
}
class deriveClass:public baseClass
{
private:
unsigned int score,number;
public:
deriveClass(unsigned int a,bool se,unsigned int sc,unsigned int nu):baseClass(a,se)
{
score = sc;
number = nu;
}
~deriveClass()
{
std::cout << "~derive Class" << std::endl;
}
unsigned int deriveGetAge(void);
bool deriveGetSex(void);
unsigned int deriveGetScore(void);
unsigned int deriveGetNumber(void);
void display(void);
};
unsigned int deriveClass::deriveGetAge(void)
{
return baseGetAge();
}
bool deriveClass::deriveGetSex(void)
{
return baseGetSex();
}
unsigned int deriveClass::deriveGetScore(void)
{
return score;
}
unsigned int deriveClass::deriveGetNumber(void)
{
return number;
}
void deriveClass::display(void)
{
std::cout << "derive Class Display" << std::endl;
}
int main(void)
{
deriveClass *clsDerive = NULL;
baseClass *clsBase = NULL;
clsDerive = new deriveClass(18,0,100,26);
clsBase = clsDerive;
std::cout << "Age = " << clsDerive->deriveGetAge()<< std::endl;
std::cout << "Sex = " << clsDerive->deriveGetSex()<< std::endl;
std::cout << "Score = " << clsDerive->deriveGetScore()<< std::endl;
std::cout << "Number = " << clsDerive->deriveGetNumber()<< std::endl;
clsBase->display();
clsDerive->display();
delete clsDerive;
clsDerive = NULL;
return 0;
}
編譯運行:
root@se7en-LIFEBOOK-LH531:~/learn/Cpp_Program# g++ class.cpp -o class
root@se7en-LIFEBOOK-LH531:~/learn/Cpp_Program# ./class
Age = 18
Sex = 0
Score = 100
Number = 26
base Class Display
derive Class Display
~derive Class
~Base Class
查看源碼,基類及其派生類都有完全一樣的函數display().當用基類指針時(見clsBase->display()),調用的是基類的的display();當用派生類指針時(見clsDerive->display()),調用的是派生類的display().這很符合常規的邏輯,什麼樣的對象,操作該對象的行爲.下面在上述的代碼裏面只多增加了關鍵字"virtual".如下:
#include <iostream>
using namespace std;
class baseClass
{
private:
unsigned int age;
bool sex;
public:
baseClass(unsigned int a,bool s)
{
age = a;
sex = s;
}
~baseClass()
{
std::cout << "~Base Class"<< std::endl;
}
unsigned int baseGetAge(void);
bool baseGetSex(void);
virtual void display(void);
};
unsigned int baseClass::baseGetAge(void)
{
return age;
}
bool baseClass::baseGetSex(void)
{
return sex;
}
void baseClass::display(void)
{
std::cout << "base Class Display" << std::endl;
}
class deriveClass:public baseClass
{
private:
unsigned int score,number;
public:
deriveClass(unsigned int a,bool se,unsigned int sc,unsigned int nu):baseClass(a,se)
{
score = sc;
number = nu;
}
~deriveClass()
{
std::cout << "~derive Class" << std::endl;
}
unsigned int deriveGetAge(void);
bool deriveGetSex(void);
unsigned int deriveGetScore(void);
unsigned int deriveGetNumber(void);
virtual void display(void);
};
unsigned int deriveClass::deriveGetAge(void)
{
return baseGetAge();
}
bool deriveClass::deriveGetSex(void)
{
return baseGetSex();
}
unsigned int deriveClass::deriveGetScore(void)
{
return score;
}
unsigned int deriveClass::deriveGetNumber(void)
{
return number;
}
void deriveClass::display(void)
{
std::cout << "derive Class Display" << std::endl;
}
int main(void)
{
deriveClass *clsDerive = NULL;
baseClass *clsBase = NULL;
clsDerive = new deriveClass(18,0,100,26);
clsBase = clsDerive;
std::cout << "Age = " << clsDerive->deriveGetAge()<< std::endl;
std::cout << "Sex = " << clsDerive->deriveGetSex()<< std::endl;
std::cout << "Score = " << clsDerive->deriveGetScore()<< std::endl;
std::cout << "Number = " << clsDerive->deriveGetNumber()<< std::endl;
clsBase->display();
clsDerive->display();
delete clsDerive;
clsDerive = NULL;
return 0;
}
編譯運行:
root@se7en-LIFEBOOK-LH531:~/learn/Cpp_Program# g++ class.cpp -o class
root@se7en-LIFEBOOK-LH531:~/learn/Cpp_Program# ./class
Age = 18
Sex = 0
Score = 100
Number = 26
derive Class Display
derive Class Display
~derive Class
~Base Class
這次兩次調用的都是派生類的display()函數.派生類的關鍵字virtual可以是忽略的不用標識的,但是爲了程序的可讀性,最好要加上.
至此,我們要調用派生類的方法有兩種,派生類對象的靜態引用和通過基類指針通過覆蓋(虛函數)的方式引用.爲了程序的可讀性,個人建議選擇靜態引用.如下:
源碼:
#include <iostream>
using namespace std;
class A
{
protected:
int x;
public:
A()
{
x =1000;
}
virtual void print()
{
std::cout << "x = " << x << std::endl;
}//虛函數
};
class B:public A
{
private:
int y;
public: B()
{
y=2000;
}
virtual void print()
{
std::cout << "y = " << y << std::endl;
}//派生虛函數
};
class C:public A
{
private:
int z;
public:
C()
{
z=3000;
}
virtual void print()
{
std::cout << "z = " << z << std::endl;
}//派生虛函數
};
int main(void)
{
A a, *pa;
B b; C c;
a.print();
b.print();
c.print(); //靜態調用
pa=&a;
pa->print();//調用類A的虛函數
pa=&b;
pa->print();//調用類B的虛函數
pa=&c;
pa->print();//調用類C的虛函數
return 0;
}
編譯運行:
root@se7en-LIFEBOOK-LH531:~/learn/Cpp_Program# g++ reference.cpp -o reference
root@se7en-LIFEBOOK-LH531:~/learn/Cpp_Program# ./reference
x = 1000
y = 2000
z = 3000
x = 1000
y = 2000
z = 3000
純虛函數:
當我們使用到繼承與派生的時候,我們比較關注的並不是基類本身,而是派生類的具體實例,這當然涉及到其中的操作集(函數).多態(覆蓋)的實現,可以實現"一個接口,多種功能實現",從而提高程序的可讀性和複用性.純虛函數就是這樣的一個"華而不實"的接口標識.定義如下:
在基類中不對虛函數給出有意義的實現,它只是在派生類中有具體的意義.這時基類中的虛函數只是一個入口,具體的目的地由不同的派生類中的對象決定.
這個虛函數稱爲純虛函數.
其定義形式如下:class <基類名>
{ virtual <類型><函數名>(<參數表>)=0;
......
};
一個簡單的示例代碼:
class A{
protected: int x;
public: A(){x =1000;}
virtual void print()=0; //定義純虛函數
};
class B:public A{ //派生類
private: int y;
public: B(){ y=2000;}
void print(){cout <<“y=”<<y<<‘\n’;}//重新定義純虛函數
};
class C:public A{ //派生類
int z;
public: C(){z=3000;}
void print(){cout <<“z=”<<z<<‘\n’;}//重新定義純虛函數
};
void main(void )
{ A *pa; B b; C c;
pa=&b; pa->print(); pa=&c; pa->print();
A a; pa=&a; pa->print( );
}
可見,重載、覆蓋二者的區別爲:
選擇時機:
重載:編譯時決定;
覆蓋:運行時決定.
存在形式:
重載:部分相同;
覆蓋:完全一致.