從C看C++之(六)多態

 類似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( );
}


小結:

    可見,重載、覆蓋二者的區別爲:

選擇時機:
    重載:編譯時決定;
    覆蓋:運行時決定.
存在形式:
    重載:部分相同;
    覆蓋:完全一致.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章