【C++】多态 ② ( 面向对象中 “ 多态 “ 的真正需求 | 面向对象 “ 多态 “ 实现 - virtual 修饰函数 | 代码示例 )

2023-10-28 10:41:05 浏览数 (2)

一、多态实现

1、面向对象中 " 多态 " 的真正需求

在上一篇博客 【C 】多态 ① ( 类型兼容性原则与函数重写 | “ 多态 “ 引入 | 函数重写 ) 中 , 进行了测试 , 在测试代码中 , 实际的对象类型是 Parent 父类类型的 ,

  • 不管是 将 Parent* 指针 指向 父类 还是 子类 ,
  • 不管是 将 Parent& 引用 指向父类 还是 子类 ,

调用的都是 父类的 函数 , 这并不是我们想要的 ;

上述测试中 , 根据实际的 对象类型 确定 重写函数 中要调用 父类 还是 子类 中的函数 , 并不是我们期望的 ;

多态 的 需求是 : 相同的调用语句 , 有多种不通的表现形态 ;

  • 父类指针 指向 父类对象 , 调用 重写函数 时 , 我们希望 调用的是 父类的函数 , 实际对象是父类 , 调用父类的函数 ;
  • 如果 父类指针 指向 子类对象 , 调用 重写函数 时 , 我们希望 调用的 是 父类的函数 , 这样才 符合 多态 的理念 ; 实际对象是子类 , 调用子类的函数 ;

通过 父类指针 可以调用 子类中重写的函数 , 根据 指针 指向的不同 , 调用不同类 的 函数 ,

  • 指针 指向 父类 , 就调用父类的函数 ;
  • 指针 指向 子类 , 就调用 子类的函数 ;

2、面向对象 " 多态 " 实现 - virtual 修饰函数

C 语言中 , 通过使用 virtual 关键字 , 实现对 多态的支持 ;

子类 重写 父类 的 函数 , 在 父类 或 子类 中 , 使用 virtual 关键字 修饰 该函数 , 即可实现 多态 的特性 ;

在 父类 中 , 使用 virtual 关键字 修饰 函数 , 子类中重写该函数时 , 可以不使用 virtual 关键字 ;

在开发时 , 建议 父类 和 子类 的 重载函数 都使用 virtual 关键字修饰 , 表示多态 , 这样能在开发者阅读代码时 , 更容易理解 此处要开始使用 多态机制了 ;

二、代码示例 - 多态实现

1、代码示例

在下面的代码中 , 使用 virtual 关键字 修饰 父类 和 子类 中的函数 , 最终实现了 多态 ;

代码示例 :

代码语言:javascript复制
#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;
}

2、执行结果

执行结果 :

代码语言:javascript复制
调用父类构造函数
调用父类构造函数
调用子类构造函数
父类 : 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 . . .

0 人点赞