目录
1、虚函数
(1)、定义:
虚函数允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。(指针指哪打哪)
在基类中用virtual声明成员函数为虚函数,在派生类中重新定义此函数并可改变其功能。当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数,但如果派生类中没有覆盖基类的虚函数,则调用时调用基类的函数定义。
(2)、覆盖和重载的区别:
重载是同一层次函数名相同;覆盖是成员函数在继承层次中函数原型完全相同。
(3)、动态绑定的实现方法:
多态主要体现在虚函数上,只要有虚函数存在,对象类型就在程序运行时动态绑定。
定义一个指向基类对象的指针,并使它指向同一类族中需要调用该函数的对象(可以是基类对象也可以是子类对象),通过该指针变量调用此虚函数。
(4)、虚函数的典型用途:
父类指针调用子类的函数。
(5)、有关虚函数的一些规定:
- 只有类的成员函数才能成为虚函数;
- 静态成员函数不能成为虚函数,因为静态成员函数不受限于某个对象。
- 内联函数不能成为虚函数,因为内联函数不能在运行中动态绑定其位置。
- 构造函数不能成为虚函数,析构函数通常是虚函数。
/*虚函数*/
#include <iostream>
using namespace std;
class CEmployee //定义CEmployee类
{
public:
int m_ID; //定义数据成员
char m_Name[128]; //定义数据成员
char m_Depart[128]; //定义数据成员
CEmployee() //定义构造函数
{
memset(m_Name,0,128); //初始化数据成员
memset(m_Depart,0,128); //初始化数据成员
}
virtual void OutputName() //定义一个虚成员函数
{
cout << "员工姓名: "<<m_Name << endl; //输出信息
}
};
class COperator:public CEmployee //从CEmployee类派生一个子类
{
public:
char m_Password[128] = {0}; //定义数据成员并初始化,否则警告C26495
void OutputName() //定义OutputName成员函数,自动成为虚函数
{
cout << "操作员姓名: "<<m_Name<< endl; //输出信息
}
};
int main(int argc, char* argv[])
{
CEmployee *pWorker = new COperator(); //定义CEmployee类型指针,调用COperator类构造函数
strcpy_s(pWorker->m_Name,128,"Mr.Wen"); //设置m_Name数据成员信息
pWorker->OutputName(); //自动用COperator类的OutputName成员函数的定义去覆盖
pWorker->CEmployee::OutputName(); //如调用基类虚函数的定义必须加载作用域
delete pWorker; //释放对象
return 0;
}
/*运行结果:
操作员姓名: Mr.Wen
员工姓名: Mr.Wen
*/
2、虚继承
(1)、虚继承的作用:
解决了多个父类派生子类的时候同一个爷爷类时,在派生子类时使其只存在一个爷爷类。
class 爷爷类
{};
class 父类1:virtual [继承方式]爷爷类
{};
class 父类2:virtual [继承方式]爷爷类
{};
class 子类:[继承方式]父类1,[继承方式]父类2,...
{
[访问控制修饰符;]
[成员声明列表]
};
(2)、带虚继承时的构造顺序 :
对于虚继承,在定义子类对象时,先调用爷爷类构造函数,然后按照派生列表生命的顺序依次调用父类的构造函数,最后调用子类的构造函数。析构时相反。
/*虚继承*/
#include <iostream>
using namespace std;
class CAnimal //定义一个动物类
{
public:
CAnimal() //定义构造函数
{
cout << "动物类被构造"<< endl;
}
void Move() //定义成员函数
{
cout << "动物能够移动"<< endl;
}
};
class CBird : virtual public CAnimal //从CAnimal类虚继承CBird类
{
public:
CBird() //定义构造函数
{
cout << "鸟类被构造"<< endl;
}
void FlyInSky() //定义成员函数
{
cout << "鸟能够在天空飞翔"<< endl;
}
void Breath() //定义成员函数
{
cout << "鸟能够呼吸"<< endl;
}
};
class CFish: virtual public CAnimal //从CAnimal类虚继承CFish
{
public:
CFish() //定义构造函数
{
cout << "鱼类被构造"<< endl;
}
void SwimInWater() //定义成员函数
{
cout << "鱼能够在水里游"<< endl;
}
void Breath() //定义成员函数
{
cout << "鱼能够呼吸"<< endl;
}
};
class CWaterBird: public CBird, public CFish //从CBird和CFish类派生子类CWaterBird
{
public:
CWaterBird() //定义构造函数
{
cout << "水鸟类被构造"<< endl;
}
void Action() //定义成员函数
{
cout << "水鸟即能飞又能游"<< endl;
}
};
int main(int argc, char* argv[]) //主函数
{
CWaterBird waterbird; //定义水鸟对象
return 0;
}
/*运行结果:
动物类被构造
鸟类被构造
鱼类被构造
水鸟类被构造 */
3、抽象类
(1)、抽象类的定义:
包含有纯虚函数的类称为抽象类。一个抽象类至少包含一个虚函数。
抽象类只能作为一个基类派生出的新的子类对待,而不能在程序中被实例化(即不能有自己的对象),但是,可以使用指向抽象类的指针。
(2)、抽象类的作用:
在开发程序的过程中并不是所有代码都是由软件构造师自己写的,有时需要调用库函数,有时分给别人写,一名软件构造师可以通过虚函数建立接口,让程序员填写代码实现接口,而自己主要负责建立抽象类。
(3)、纯虚函数:
是指被标明为没有具体实现的虚成员函数,它不具备函数的功能。
许多情况下,在基类中不能给出虚函数一个有意义的定义,这时可以在基类中将它说明为纯虚函数,由派生类实现。纯虚函数不能被直接调用,仅起到一个与派生类相一致的接口的作用。
(4)、声明纯虚函数一般表达式:
virtual 类型 函数名(参数列表)=0; //没有{}即函数体
纯虚函数不能被继承。当基类是抽象类时,在派生类中必须给出基类中纯虚函数的定义,或在该类中再声明其为纯虚函数。只有在派生类中给出基类所有纯虚函数的实时,该派生类才不再成为抽象类。
/*抽象类*/
#include <iostream>
using namespace std;
class CFigure //定义类
{
public:
virtual double getArea() =0; //虚函数=抽象成员函数
};
const double PI=3.14;
class CCircle : public CFigure //定义派生类
{
private:
double m_dRadius; //定义派生类数据成员
public:
CCircle(double dR) //构造函数
{
m_dRadius=dR;
}
double getArea() //成员函数
{
cout << "圆的面积是=";
return m_dRadius*m_dRadius*PI;
}
};
class CRectangle : public CFigure //定义派生类
{
protected:
double m_dHeight,m_dWidth;
public:
CRectangle(double dHeight,double dWidth)//定义构造函数
{
m_dHeight=dHeight;
m_dWidth=dWidth;
}
double getArea() //定义成员函数
{
cout << "矩形的面积是=";
return m_dHeight*m_dWidth;
}
};
int main()
{
/*CFigure figure;*/ //纯虚函数不能实例化没有自己的对象,但可以有自己的指针,否则E0322
CFigure *fg1; //定义基类对象实例的指针
fg1= new CRectangle(4.0,5.0); //指针指向派生类所占内存空间
cout << fg1->getArea() << endl;
delete fg1; //释放指针
CFigure *fg2; //定义基类对象实例的指针
fg2= new CCircle(4.0); //指针指向派生类所占内存空间
cout << fg2->getArea() << endl;
delete fg2; //释放指针
}
/*运行结果:同一基类不同的实例指针,通过基类内虚函数指向不同的派生类即使函数名相同也会获得不同的结果
矩形的面积是=20
圆的面积是=50.24 */
4、总结
特征1 | 特征2 | 特征3 | 用途 | |
虚函数 | 父类成员函数被声明virtual | 所有子类同名函数自动成为virtual | 动态绑定:通过基类指针或引用来访问基类和派生类中的同名函数。(指针指哪打哪) | 父类指针调用子类成员 |
虚继承 | 把基类声明称virtual | 多个基类派生子类并且多个基类又派生于共同基类(爷类) | 创建子类对象时,存在多次构建爷爷类的情况 | 解决了多个父类派生子类的时候同一个爷爷类时,在派生子类时使其只存在一个爷爷类。 |
抽象类 | 类域里包含纯虚函数(类域可以是基类也可以子类,通常基类) | 当基类是抽象类时,在派生类中必须给出基类中纯虚函数的定义,或在该类中再声明其为纯虚函数。 | 纯虚函数不能被直接调用,没有自己的成员对象,仅起到一个与派生类相一致的接口的作用。 | 软件工程分工协作 |