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


小结:

    可见,重载、覆盖二者的区别为:

选择时机:
    重载:编译时决定;
    覆盖:运行时决定.
存在形式:
    重载:部分相同;
    覆盖:完全一致.


你可能感兴趣的:(从C看C++之(六)多态)