在上一篇博客 【C++】多态 ① ( 类型兼容性原则与函数重写 | “ 多态 “ 引入 | 函数重写 )
中 , 进行了测试 , 在测试代码中 , 实际的对象类型是 Parent 父类类型的 ,
调用的都是 父类的 函数 , 这并不是我们想要的 ;
上述测试中 , 根据实际的 对象类型 确定 重写函数 中要调用 父类 还是 子类 中的函数 , 并不是我们期望的 ;
多态 的 需求是 : 相同的调用语句 , 有多种不通的表现形态 ;
通过 父类指针 可以调用 子类中重写的函数 , 根据 指针 指向的不同 , 调用不同类 的 函数 ,
C++ 语言中 , 通过使用 virtual 关键字 , 实现对 多态的支持 ;
子类 重写 父类 的 函数 , 在 父类 或 子类 中 , 使用 virtual 关键字 修饰 该函数 , 即可实现 多态 的特性 ;
在 父类 中 , 使用 virtual 关键字 修饰 函数 , 子类中重写该函数时 , 可以不使用 virtual 关键字 ;
在开发时 , 建议 父类 和 子类 的 重载函数 都使用 virtual 关键字修饰 , 表示多态 , 这样能在开发者阅读代码时 , 更容易理解 此处要开始使用 多态机制了 ;
在下面的代码中 , 使用 virtual 关键字 修饰 父类 和 子类 中的函数 , 最终实现了 多态 ;
代码示例 :
#include "iostream"
using namespace std;
// 父类
class Parent {
public:
Parent(int a)
{
x = a;
cout << "调用父类构造函数" << endl;
}
virtual void print()
{
cout << "父类 : x = " << x << endl;
}
public:
int x;
};
// 子类
class Child : public Parent {
public:
// 在子类构造函数初始化列表中 调用 父类构造函数
Child(int a, int b) : Parent(a)
{
y = b;
cout << "调用子类构造函数" << endl;
}
// 子类重写父类的 print 函数
virtual void print()
{
cout << "子类 : x = " << x << " , y = " << y << endl;
}
public:
int y;
};
// 父类指针作为函数参数
// 分别传入 子类对象 和 父类对象 地址
void fun(Parent* p)
{
p->print();
}
// 父类引用作为函数参数
// 分别传入 子类对象 和 父类对象 本身
void fun(Parent& p)
{
p.print();
}
int main() {
// 定义父类指针
Parent* p = NULL;
// 定义 父类 和 子类对象
Parent parent(1);
Child child(1, 2);
// 1. 调用父类对象的 print 函数
// 结果 - `父类 : x = 1`
parent.print();
// 2. 调用子类对象的 print 函数
// 结果 - `子类 : x = 1 , y = 2`
child.print();
// 3. 将 p 指针指向 父类对象
// 通过 p 指针 调用指向对象的 print 函数
// 结果 - `父类 : x = 1`
p = &parent;
p->print();
// 4. 将 p 指针指向 子类对象
// 通过 p 指针 调用指向对象的 print 函数
// 结果 - `子类 : x = 1 , y = 2`
// 将 子类对象 地址赋值给了 p 指针
// 根据多态机制 , 此时调用的是子类的函数
p = &child;
p->print();
// 5. 将 Parent 引用 指向 父类对象
// 结果 - `父类 : x = 1`
Parent& p2 = parent;
p2.print();
// 5. 将 Parent 引用 指向 子类对象
// 结果 - `子类 : x = 1 , y = 2`
Parent& p3 = child;
p3.print();
// 6. 父类指针作为函数参数
// 传入父类对象地址 , 结果 - `父类 : x = 1`
fun(&parent);
// 传入子类对象地址 , 结果 - `子类 : x = 1 , y = 2`
fun(&child);
// 7. 父类引用作为函数参数
// 传入父类对象本身 , 结果 - `父类 : x = 1`
fun(parent);
// 传入子类对象本身 , 结果 - `子类 : x = 1 , y = 2`
fun(child);
// 控制台暂停 , 按任意键继续向后执行
system("pause");
return 0;
}
执行结果 :
调用父类构造函数
调用父类构造函数
调用子类构造函数
父类 : x = 1
子类 : x = 1 , y = 2
父类 : x = 1
子类 : x = 1 , y = 2
父类 : x = 1
子类 : x = 1 , y = 2
父类 : x = 1
子类 : x = 1 , y = 2
父类 : x = 1
子类 : x = 1 , y = 2
Press any key to continue . . .