派生类对象替换基类对象
一个替换原则
- 凡是基类对象出现的场合都可以用公有派生类对象取代
三个替换形式
- 派生类对象给基类对象赋值
- 派生类对象可以初始化基类对象的引用
- 可以令基类对象的指针指向派生类对象,即将派生类对象的地址传递给基类指针
派生类对象替换基类对象举例
设计一个手机类mobile,使其能派生出两种发射制式(GSM或者CDMA)的手机类mobilegsm
和mobilecdma
派生类对象能够依据各自特征实现显示发射制式的功能,并实现运行时的动态绑定
通过将基类对象或者指针在运行时指向不同的派生类对象实现动态绑定
#include <iostream>
using namespace std;
class mobile
{
public:
mobile(){}
char mynumber[11]; //机主的电话号码
void showinfo() //显示制式
{cout<<”The phone is mobile”<<endl;}
};
class mobilegsm:public mobile
{
public:
mobilegsm(){}
void showinfo() //显示制式
{cout<<”The phone is mobilegsm”<<endl;}
};
class mobilecdma:public mobile
{
public:
mobilecdma(){}
void showinfo() //显示制式
{cout<<”The phone is mobilecdma”<<endl;}
};
主函数测试
int main()
{
mobile m, *p1; //基类对象指针p1,基类对象m
mobilegsm gsm;
mobilecdma cdma;
m = gsm; //用gsm类对象给mobile类对象赋值
m.showinfo();
m = cdma; //用cdma类对象给mobile类对象赋值
m.showinfo();
p1 = &gsm; //用gsm类对象地址给mobile类对象赋值
p1->showinfo();
p1 = &cdma; //用cdma类对象地址给mobile类对象赋值
p1->showinfo();
mobile &p4=gsm; //以gsm类对象初始化mobile类引用
p4.showinfo();
mobile &p5=cdma; //以cdma类对象初始化mobile类引用
p5.showinfo();
return 0;
}
可以看到输出的信息都是调用基类中的showinfo
成员函数
再举上一节中的宠物类,将virtual
关键字去掉
#include <iostream>
using namespace std;
class Pet //基类:宠物类
{
public:
void Speak() { cout << "How does a pet speak ?" << endl; }
};
class Cat : public Pet //派生类:猫类
{
public:
void Speak() { cout << "miao!miao!" << endl; }
};
class Dog : public Pet //派生类:狗类
{
public:
void Speak() { cout << "wang!wang!" << endl; }
};
主函数测试
int main()
{
Pet *p1, *p2, *p3, obj; //基类对象指针p1, 基类对象obj
Dog dog1;
Cat cat1;
obj = dog1; //用Dog类对象给Pet类对象赋值
obj.Speak();
p1 = &cat1; //用Cat类对象地址给基类指针赋值
p1->Speak();
p1 = &dog1; //用Dog类对象地址给基类指针赋值
p1->Speak();
p2 = new Cat; //动态生成Cat类对象
p2->Speak();
p3 = new Dog; //动态生成Dog类对象
p3->Speak();
Pet &p4 = cat1; //以Cat类对象初始化Pet类引用
p4.Speak();
return 0;
}
可以看到,与上面的例子一样,输出的都是基类pet
中的Speak()
两个程序运行共性
手机类或者宠物类
- 尽管派生类对象赋值给了基类对象(指针或引用)
- 但基类对象都不调用派生类同名成员函数
- 只调用基类的同名成员函数
问题
- 如何让基类对象(指针或引用)能够调用派生类的函数呢?
- 也就是说如何实现基类对象指针与派生类对象动态绑定?
虚函数
虚函数的定义
什么是虚函数?
- 在函数定义的头部加上virtual,该函数就是虚函数
- 事实上,在某基类中声明为virtual并在一个或多个派生类中被重新定义的同名成员函数,称为虚函数
虚函数的定义:
virtual 函数返回类型 函数名(参数表)
{
函数体
}
虚函数的用途:
- 实现运行时的多态性,即通过指向派生类的基类指针,访问派生类中同名覆盖成员函数
虚函数例子
上述手机类以及宠物类改成 虚函数定义
- 手机类
#include <iostream>
using namespace std;
class mobile
{
public:
char mynumber[11]; //机主的电话号码
virtual void showinfo() //显示制式
{cout << "The phone is mobile" << endl;}
};
class mobilegsm:public mobile
{
public:
virtual void showinfo() //显示制式
{cout << "The phone is mobilegsm" << endl;}
};
class mobilecdma:public mobile
{
public:
virtual void showinfo() //显示制式
{cout << "The phone is mobilecdma" << endl;}
};
主函数测试
int main()
{
mobile m,*p1; //基类对象指针p1,基类对象m
mobilegsm gsm;
mobilecdma cdma;
m = gsm; //用gsm类对象给mobile类对象赋值
m.showinfo();
m = cdma; //用cdma类对象给mobile类对象赋值
m.showinfo();
p1 = &gsm; //用gsm类对象地址给mobile类对象赋值
p1->showinfo();
p1=&cdma; //用cdma类对象地址给mobile类对象赋值
p1->showinfo();
mobile &p4=gsm; //以gsm类对象初始化mobile类引用
p4.showinfo();
mobile &p5=cdma; //以cdma类对象初始化mobile类引用
p5.showinfo();
return 0;
}
宠物类
#include <iostream>
using namespace std;
class Pet //基类
{
public:
virtual void Speak()
{ cout << "How does a pet speak ?" << endl; }
};
class Cat : public Pet //派生类
{
public:
virtual void Speak()
{ cout<<"miao!miao!"<<endl; }
};
class Dog : public Pet //派生类
{
public: virtual void Speak()
{ cout<<"wang!wang!"<<endl; }
};
主函数测试
int main()
{
Pet *p1, *p2, *p3, obj; //基类对象指针p1, 基类对象obj
Dog dog1;
Cat cat1;
obj = dog1; //用Dog类对象给Pet类对象赋值
obj.Speak();
p1 = &cat1; //用Cat类对象地址给基类指针赋值
p1->Speak();
p1 = &dog1; //用Dog类对象地址给基类指针赋值
p1->Speak();
p2 = new Cat; //动态生成Cat类对象
p2->Speak();
p3 = new Dog; //动态生成Dog类对象
p3->Speak();
Pet &p4 = cat1; //以Cat类对象初始化Pet类引用
p4.Speak();
return 0;
}
虚函数的使用限制
- 应通过指针或引用调用虚函数,而不要以对象名调用虚函数
Pet obj;
Dog dog1;
obj = dog1;
obj.Speak(); //执行的是基类Speak()函数
Pet *p1 = &dog1;
p1->Speak();
- 在派生类中重定义的基类虚函数仍为虚函数,同时可以省略virtual关键字
- 不能定义虚构造函数,可以定义虚析构函数
虚函数是多态的一种实现形式
作用是实现函数的覆盖
写法是在基类的成员函数之前加上关键字virtual
今后在类的继承中,基类尽量使用虚函数