C++ 多态性示例

什么是多态性 C++?

In C++多态性会导致成员函数根据调用它的对象而表现不同。多态性是一个希腊词,意思是有多种形式。当您拥有通过继承而关联的类层次结构时,就会发生这种情况。

例如,假设我们有函数 makeSound()。当猫调用此函数时,它会发出喵喵的声音。当牛调用同一函数时,它会发出哞哞的声音。

多态性 C++

虽然我们有一个函数,但是它在不同情况下的行为却不同。该函数有多种形式;因此,我们实现了多态性。

多态性的类型

C++ 支持两种类型的多态性:

  • 编译时多态性,以及
  • 运行时多态性。

多态性的类型

编译时多态性

您可以通过匹配参数的数量和类型来调用重载函数。这些信息在编译时存在。这意味着 C++ 编译器将在编译时选择正确的函数。

编译时多态性是通过函数重载和运算符重载实现的。

函数重载

当我们拥有许多名称相似但参数不同的函数时,就会发生函数重载。这些参数的数量或类型可能不同。

例子1

#include <iostream> 
using namespace std;

void test(int i) {
	cout << " The int is " << i << endl;
}
void test(double  f) {
	cout << " The float is " << f << endl;
}
void test(char const *ch) {
	cout << " The char* is " << ch << endl;
}

int main() {
	test(5);
	test(5.5);
	test("five");
	return 0;
}

输出:

函数重载

以下是代码截图:

函数重载

代码说明:

  1. 将 iostream 头文件包含到我们的代码中。我们将能够使用它的功能。
  2. 在我们的代码中包含 std 命名空间。我们将能够在不调用它的情况下使用它的类。
  3. 创建一个名为 test 的函数,该函数接受一个整数参数 i。{ 标记函数 test 主体的开始。
  4. 如果上述函数测试被调用,则执行的语句。
  5. 以上功能测试主体结束。
  6. 创建一个名为 test 的函数,该函数采用浮点参数 f。{ 标记函数 test 主体的开始。
  7. 如果上述函数测试被调用,则执行的语句。
  8. 上述函数测试主体结束。
  9. 创建一个名为 test 的函数,该函数接受一个字符参数 ch。{ 标记函数 test 主体的开始。
  10. 如果上述函数测试被调用,则执行的语句。
  11. 上述函数测试主体结束。
  12. 调用 main() 函数。{ 标记函数主体的开始。
  13. 调用函数 test,并将 5 作为参数值传递给它。这将调用接受整数参数的测试函数,即第一个测试函数。
  14. 调用函数 test,并将 5.5 作为参数值传递给它。这将调用接受浮点参数的测试函数,即第二个测试函数。
  15. 调用函数 test,并将 5 作为参数值传递给它。这将调用接受字符参数的测试函数,即第三个测试函数。
  16. 如果程序运行成功,必须返回一个值。
  17. main() 函数主体的结束。

我们有三个函数,它们的名字相同,但参数的类型不同。我们实现了多态性。

Operator 过载

In Opera重载,我们为 C++ 运算符。它还改变了运算符的工作方式。例如,我们可以定义 + 运算符来连接两个字符串。我们知道它是用于添加数值的加法运算符。按照我们的定义,当它放在整数之间时,它会将它们相加。当它放在字符串之间时,它会将它们连接起来。

例子2

#include<iostream> 
using namespace std;

class ComplexNum {
private:
	int real, over;
public:
	ComplexNum(int rl = 0, int ov = 0) {
		real = rl;   
		over = ov; 
	}

	ComplexNum operator + (ComplexNum const &obj) {
		ComplexNum result;
		result.real = real + obj.real;
		result.over = over + obj.over;
		return result;
	}
	void print() { 
		cout << real << " + i" << over << endl; 
	}
};
int main()
{
	ComplexNum c1(10, 2), c2(3, 7);
	ComplexNum c3 = c1+c2;
	c3.print();
}

输出:

Operator 过载

以下是代码截图:

Operator 过载

Operator 过载

代码说明:

  1. 将iostream头文件包含到我们的程序中才能使用其功能。
  2. 将 std 命名空间包含到我们的程序中,以便在不调用它的情况下使用它的类。
  3. 创建一个名为 ComplexNum 的类。{ 标记类主体的开始。
  4. 使用 private 访问修饰符将变量标记为私有,这意味着它们只能从类内部访问。
  5. 定义两个整数变量,real 和 over。
  6. 使用 public 访问修饰符将构造函数标记为公共的,这意味着它甚至可以从构造函数外部访问。 .
  7. 创建类构造函数并初始化变量。
  8. 初始化变量real的值。
  9. 初始化变量的值。
  10. 构造函数主体结束。
  11. 我们需要覆盖 + 运算符的含义。
  12. 创建 ComplexNum 类型的数据类型结果。
  13. 对复数使用 + 运算符。此行将一个数的实部添加到另一个数的实部。
  14. 对复数使用 + 运算符。此行将一个数的虚部添加到另一个数的虚部。
  15. 程序执行成功后将返回变量result的值。
  16. 结束定义+运算符的新含义,即重载。
  17. 调用 print() 方法。
  18. 在控制台上打印加法后的新复数。
  19. print() 函数体结束。
  20. ComplexNum 类主体结束。
  21. 调用 main() 函数。
  22. 传递要相加的实部和复部的值。c1 的第一部分将添加到 c2 的第一部分,即 10+3。c1 的第二部分将添加到 c 的第二部分,即 2+7。
  23. 使用重载 + 运算符执行运算并将结果存储在变量 c3 中。
  24. 在控制台上打印变量 c3 的值。
  25. main() 函数体结束。

运行时多态

当对象的方法在运行时而不是在编译时被调用时,就会发生这种情况。运行时多态性是通过函数重写实现的。要调用的函数是在运行时建立的。

函数覆盖

函数重写是指在派生类中对基类的函数进行新的定义。此时,我们可以说基类函数被重写了。

例如:

#include <iostream>
using namespace std;
class Mammal {

public:
	void eat() {

		cout << "Mammals eat...";
	}

};

class Cow: public Mammal {

public:
	void eat() {

		cout << "Cows eat grass...";
	}
};
int main(void) {

	Cow c = Cow();

	c.eat();

	return 0;

}

输出:

函数覆盖

以下是代码截图:

函数覆盖

代码说明:

  1. 将iostream头文件导入到我们的程序中就可以使用它的功能。
  2. 将 std 命名空间包含到我们的程序中,以便在不调用它的情况下使用它的类。
  3. 创建一个名为 Mammal 的类。{ 标记类主体的开始。
  4. 使用公共访问修饰符将我们要创建的函数设置为可公开访问。它将可从此类外部访问。
  5. 创建一个名为 eat 的公共函数。{ 标记函数体的开始。
  6. 在调用函数eat()时打印添加到cout函数的语句。
  7. eat() 函数体结束。
  8. 哺乳动物类身体的结束。
  9. 创建一个名为 Cow 的类,该类继承自 Mammal 类。Cow 是派生类,而 Mammal 是基类。{ 标记此类的开始。
  10. 使用公共访问修饰符将我们要创建的函数标记为可公开访问。它将可从此类外部访问。
  11. 覆盖基类中定义的函数 eat()。{ 标记函数体的开始。
  12. 调用此函数时要在控制台上打印的语句。
  13. eat() 函数体结束。
  14. Cow 类的主体部分结束。
  15. 调用 main() 函数。{ 标记此函数主体的开始。
  16. 创建 Cow 类的一个实例并将其命名为 c。
  17. 调用Cow类中定义的eat()函数。
  18. 程序成功完成后必须返回一个值。
  19. main() 函数结束。

C++ 虚拟功能

虚函数是实现运行时多态性的另一种方式 C++。它是在基类中定义并在派生类中重新定义的特殊函数。要声明虚函数,应使用 virtual 关键字。该关键字应位于基类中函数声明的前面。

如果继承了虚函数类,则虚类会重新定义虚函数以满足其需要。例如:

#include <iostream>  
using namespace std;
class ClassA {
		public:
		virtual void show() {
			cout << "The show() function in base class invoked..." << endl;
		}
	};
	class ClassB :public ClassA {
	public:
		void show() 	{
			cout << "The show() function in derived class invoked...";
		}
	};
	int main() {
		ClassA* a;   
		ClassB b;
		a = &b;
		a->show();      
	}

输出:

C++ 虚拟功能

以下是代码截图:

C++ 虚拟功能

代码说明:

  1. 在代码中包含 iostream 头文件以使用其功能。
  2. 在我们的代码中包含 std 命名空间,以便使用它的类而不调用它。
  3. 创建一个名为 ClassA 的类。
  4. 使用公共访问修饰符将类成员标记为可公开访问。
  5. 创建一个名为 show() 的虚拟函数。它将是一个公共函数。
  6. 调用 show() 时要打印的文本。endl 是 C++ 关键字,表示结束行。它将鼠标光标移动到下一行。
  7. 虚函数 show() 函数体结束。
  8. ClassA 类主体结束。
  9. 创建一个名为 ClassB 的新类,该类继承自 ClassA 类。ClassA 成为基类,而 ClassB 成为派生类。
  10. 使用公共访问修饰符将类成员标记为可公开访问。
  11. 重新定义基类中派生的虚函数show()。
  12. 当调用派生类中定义的 show() 函数时要在控制台上打印的文本。
  13. show() 函数体结束。
  14. 派生类 ClassB 的主体结束。
  15. 调用 main() 函数。程序逻辑应添加到其主体内。
  16. 创建一个名为a的指针变量。它指向名为ClassA的类。
  17. 创建名为 ClassB 的类的实例。该实例被命名为 b。
  18. 将地址b中存储的值赋给变量a。
  19. 调用派生类中定义的 show() 函数。已实现后期绑定。
  20. main() 函数体结束。

编译时多态性与运行时多态性

以下是两者之间的主要区别:

编译时多态 运行时多态性
这也被称为早期绑定或静态多态性 这也被称为后期/动态绑定或动态多态性
该方法在编译时被调用 该方法在运行时被调用
通过函数重载和运算符重载实现 通过方法重写和虚函数实现
例如,方法重载。许多方法可能具有相似的名称,但参数的数量或类型不同 例如,方法覆盖。许多方法可能具有相似的名称和相同的原型。
由于方法发现是在编译时完成的,因此执行速度更快 由于方法发现是在运行时完成的,因此执行速度较慢。
Less 由于在编译时所有信息都是已知的,因此为解决问题提供了灵活性。 由于方法是在运行时发现的,因此为解决复杂问题提供了很大的灵活性。

总结

  • 多态意味着具有多种形式。
  • 当存在通过继承而相关的类的层次结构时,就会发生这种情况。
  • 通过多态性,函数可以根据调用它的对象表现出不同的行为。
  • 在编译时多态性中,要调用的函数是在编译时确定的。
  • 在运行时多态中,要调用的函数是在运行时建立的。
  • 编译时多态性是通过函数重载和运算符重载来确定的。
  • 在函数重载中,有许多函数具有相似的名称,但参数不同。
  • 参数的数量或类型可以不同。
  • 在运算符重载中,为 C++ 运营商.
  • 运行时多态性是通过函数重写实现的。
  • 在函数覆盖中,派生类为基类中定义的函数赋予新的定义。