最近在开发一个项目要求主程序可以应用到不同的子类对象,进而引出了C++的继承和多态,通过自己的学习并实际应用,将对多态和继承的理解总结出来,算是对自己学习内容的总结,也希望能对新学者有所帮助。
继承就是父子间的关系,一个父类可以派生出多个子类,这些子类有自己的特性,但也有共同的属性。举个例子,我们经常看到的各种鱼,草鱼、鲢鱼、鲫鱼。。。。,我们可以轻易分辨它们是因为它们有各自的特点,但从另一方面来看,它们又有共同的特点:都在水中生活,靠鳃呼吸,有鳞片,这就是共性。如果我们将所有鱼的共性抽出,组成一个类,那么我们就得到了一个描述鱼类的类,这就叫父类,所有鱼都必须包含这些特性。回头再来看具体的鱼,草鱼的体型长、鳞片大;鲢鱼鳞片小而多,鲫鱼体型小,这些就是他们各自的属性,那么我们就可以分别定义草鱼类、鲢鱼类、鲫鱼类,在这些类中,包含它们各自的特性并且,在定义类的同时我们规定它们都继承自“鱼”这个类,也就是把它们看作“鱼”这个类的孩子,这样我们即描述了它作为鱼所具备的属性,也描述了它们区别鱼其它鱼类的特点。在编程中,这样做的目的之一就是为了减少代码量,减少重复劳动。
再来看多态。既然我们规定了“鱼”类是父亲,而“草鱼”类,“鲢鱼”类,“鲫鱼”类是它的三个孩子,那么我们会想,在一段程序中要对某一条鱼进行操作,比如烹饪,不同的鱼有不同的做法,但我们又事先不知道选定的是哪种鱼,怎么办呢?是不是要根据不同的鱼重复写三次代码呢?答案是否定的,原因来自于C++ 允许我们使用父类指针指向子类实体。具体操作就是我们定义“鱼”类的指针,但赋值时赋给它选定种类的鱼的实体地址,这样就可以避免重复三次编写烹饪过程的代码。
理论不多说,上代码:
Cfather.h
#pragma once
#include <iostream>
using namespace std;
class Cfather
{
protected:
int common_1;
int common_2;
double common_3;
public:
Cfather(void);
Cfather(int c1,int c2,double c3);
virtual void fun(); //父类定义虚函数,让子类来实现
~Cfather(void);
};
Cfather.cpp
#include "Cfather.h"
Cfather::Cfather(void)
{
}
Cfather::~Cfather(void)
{
}
Cfather::Cfather(int f1, int f2, double f3):common_1(f1),common_2(f2),common_3(f3) //成员初始化
{
}
void Cfather::fun() //父类的虚函数没有代码
{
}
Son1.h
#pragma once
#include "cfather.h"
class CSon1 :
public Cfather //继承父类属性
{
protected:
int s1;
public:
CSon1(void);
CSon1(int s1_1,int s1_2,double s1_3,int s1_4);
~CSon1(void);
virtual void fun(); //实现父类虚方法
};
Son1.cpp
#include "Son1.h"
CSon1::CSon1(void)
{
}
CSon1::~CSon1(void)
{
}
CSon1::CSon1(int s1_1,int s1_2, double s1_3,int s1_4):Cfather(s1_1,s1_2,s1_3)
{
s1 = s1_4;
}
void CSon1::fun()
{
cout<<"Cfather的属性:"<<endl;
cout<<"common_1 = "<<common_1<<endl;
cout<<"common_2 = "<<common_2<<endl;
cout<<"common_3 = "<<common_3<<endl;
cout<<"CSon1的属性:"<<endl;
cout<<"s1 = "<<s1<<endl;
}
Son2.h
#pragma once
#include "cfather.h"
class CSon2 :
public Cfather
{
protected:
int s2;
public:
CSon2(void);
CSon2(int s2_1,int s2_2,double s2_3,int s2_4);
~CSon2(void);
void fun(); //实现父类虚方法
};
Son2.cpp
#include "Son2.h"
CSon2::CSon2(void)
{
}
CSon2::~CSon2(void)
{
}
CSon2::CSon2(int s2_1,int s2_2, double s2_3,int s2_4):Cfather(s2_1,s2_2,s2_3)
{
s2 = s2_4;
}
void CSon2::fun()
{
cout<<"Cfather的属性:"<<endl;
cout<<"common_1 = "<<common_1<<endl;
cout<<"common_2 = "<<common_2<<endl;
cout<<"common_3 = "<<common_3<<endl;
cout<<"CSon2的属性:"<<endl;
cout<<"s2 = "<<s2<<endl;
}
Son3.h
#pragma once
#include "cfather.h"
class CSon3 :
public Cfather
{
protected:
int s3;
public:
CSon3(void);
CSon3(int s3_1,int s3_2,double s3_3,int s3_4);
~CSon3(void);
virtual void fun();//实现父类虚方法
};
Son3.cpp
#include "Son3.h"
CSon3::CSon3(void)
{
}
CSon3::~CSon3(void)
{
}
CSon3::CSon3(int s3_1,int s3_2, double s3_3,int s3_4):Cfather(s3_1,s3_2,s3_3)
{
s3 = s3_4;
}
void CSon3::fun()
{
cout<<"Cfather的属性:"<<endl;
cout<<"common_1 = "<<common_1<<endl;
cout<<"common_2 = "<<common_2<<endl;
cout<<"common_3 = "<<common_3<<endl;
cout<<"CSon3的属性:"<<endl;
cout<<"s3 = "<<s3<<endl;
}
main.cpp
#include "Cfather.h"
#include "Son1.h"
#include "Son2.h"
#include "Son3.h"
void main()
{
//Cfather指针指向CSon1类
cout<<"Cfather指针指向CSon1类"<<endl;
Cfather *p = new CSon1(11,12,13.1,14);
p->fun();
cout<<endl;
//Cfather指针指向CSon2类
cout<<"Cfather指针指向CSon2类"<<endl;
p = new CSon2(21,22,23.1,24);
p->fun();
cout<<endl;
//Cfather指针指向CSon3类
cout<<"Cfather指针指向CSon3类"<<endl;
p = new CSon3(31,32,33.1,34);
p->fun();
}
运行结果:
ok,代码很简单,同一个父类指针p多次赋值为不同的子类对象地址,并且每次赋值时给父类的属性不同的值,结果显示如我们输入的一样,这样就实现了用一个父类指针调用不同子类的代码,在这个小程序中我们复用的就是父类指针p和虚方法fun(),工程小时无法体现多态的好处,但试想,若我们要用程序控制机器运行,所有机器运行过程一直,但不同类型的机器具体控制代码不一致,则就可以把公用的控制流程做成抽象类,所有机器类由该类派生,调用时使用这个公用类的指针,这样就可以实现控制流程的完全复用,而且后续添加机器只需按公用类接口编写代码即可实现新机器无缝添加。这就是多态的最大的用处(个人看法)。
注:多态中,父类指针只能调用父类的属性和在父类中定义的(虚)方法,子类本身的属性以及子类自定义的方法是无法调用的,在这个例子中,指针p无法调用s1,s2,s3,所以,能复用的都是公共方法。