在实际开发中,可能会遇到这样的情况,即希望同一个方法在派生类和基类中的行为是不同的,换句话来说,方法的行为应取决于调用该方法的对象。这种较复杂的行为称为多态。即同一个方法的行为随上下文而异。有两种重要的机制可用于实现多态公有继承。
- 在派生类中重新定义基类的方法。
- 使用虚方法。
一,People类和Student类
- people.h
#ifndef PEOPLE_H
#define PEOPLE_H
#include <iostream>
using namespace std;
class People
{
public:
People();
People(const string &name, const string &age);
virtual ~People();
void say();
virtual void display();
protected:
string m_name;
string m_age;
};
class Student : public People
{
public:
Student(const string &name, const string &age, const string &score);
Student(const string &score, const People *p);
void study(const string &course);
virtual void display();
private:
string m_score;
};
#endif // PEOPLE_H
关于程序清单,有以下说明:
- Student类在People类的基础上添加了一个私有数据成员和一个公有成员函数
- Student类和People类都声明了display方法。但Student对象和People对象的这些方法的行为是不同的。
两个display原型表明将有两个独立的方法定义.基类版本的限定名为People::display(), 派生类版本的限定名为Student::display()。程序将使用对象类型来确定使用哪个版本。
People people("clc", "25");
Student student("kalu", "24", "100");
people.display(); // clc 25 使用People::display()
student.display(); // kalu 24 100 使用Student::display()
- Student类在声明display是使用了关键字virtual, 该方法称为虚方法。
该项要比前两点复杂,如果方法是用过引用或指针而不是对象调用的,它将确定使用哪一种方法。如果没有使用virtual关键字,程序将根据引用类型或指针类型选择方法;如果使用了virtual,程序将根据引用类型或指针指向的对象的类型来选择方法。
如果display()不是虚的,则程序的行为如下:引用变量的类型为People,所以选择了People::display(),指针与此类似。
People people("clc", "25");
Student student("kalu", "24", "100");
People &r_people = people;
People &r_student = student;
r_people.display(); // clc 25 使用People::display()
r_student.display(); // kalu 24 使用People::display()
如果display()是虚的,则程序的行为如下:两个引用的类型都是People,但是r_student引用的是一个Student对象,所有使用的是Student::display()。指针与此类似。
People people("clc", "25");
Student student("kalu", "24", "100");
People &r_people = people;
People &r_student = student;
r_people.display(); // clc 25 使用People::display()
r_student.display(); // kalu 24 100 使用Student::display()
虚函数的这种行为非常方便,因此,经常在基类中将派生类会重新定义的方法声明为虚方法。方法在基类中被声明为虚的后,它在派生类将自动称为虚方法。
4. People类还声明了一个虚析构函数,这样做是为了确保释放对象时按正确的顺序调用析构函数。
- 类实现 people.cpp
#include "people.h"
People::People()
{
}
People::People(const string &name, const string &age)
{
m_name = name;
m_age = age;
}
People::~People()
{
}
void People::say()
{
cout << m_name << " is speak" << endl;
}
void People::display()
{
cout << m_name << " " << m_age << endl;
}
Student::Student(const string &name, const string &age, const string &score)
: People (name, age)
{
m_score = score;
}
void Student::study(const string &course)
{
cout << m_name << " is study " << course << endl;
}
void Student::display()
{
cout << m_name << " " << m_age << " " << m_score << endl;
}
- 派生类构造函数在初始化基类私有数据时,采用的是成员初始化列表,将基类信息传递给基类构造函数。
- 非构造函数不能使用成员初始化列表。
二,使用People类和Student类
#include <iostream>
#include "people.h"
using namespace std;
int main()
{
People people("clc", "25");
Student student("kalu", "24", "100");
people.say();
student.say(); // 继承基类的方法
people.display();
student.display(); // 重写基类的虚方法
student.study("English"); // 派生类新增方法
return 0;
}
结果:
clc is speak
kalu is speak
clc 25
kalu 24 100
kalu is study English
三,演示虚方法的行为
假设要同时保存所有人的信息,如果能使用一个数组保存People和Student对象,将很有帮助,但这是不可能的,数组中所有元素的类型必须相同,而People和Student是不同的类型。然而,可以创建指向People的指针数组。这样,每个元素的类型都相同,但由于使用的是公有继承模型,因此People指针既可以指向People对象,又可以指向Student对象。因此,可以使用一个数组来表示多种类型的对象,这就是多态性。
#include <iostream>
#include "people.h"
using namespace std;
const int CLIENTS = 3;
int main()
{
People people("clc", "25");
Student student1("kalu", "24", "100");
Student student2("alli", "25", "101");
People * p_clients[CLIENTS];
p_clients[0] = &people;
p_clients[1] = &student1;
p_clients[2] = &student2;
for (int i = 0; i < CLIENTS; i++)
{
p_clients[i]->display();
}
return 0;
}
clc 25
kalu 24 100
alli 25 101