C++ 多态与示例
什么是 C++ 中的多态性?
在 C++ 中,多态性会导致成员函数的行为基于调用/调用它的对象而有所不同。多态性是一个希腊词,意思是有多种形式。当您具有通过继承相关的类层次结构时,就会发生这种情况。
例如,假设我们有函数 makeSound()。当一只猫调用这个函数时,它会发出喵喵的声音。当一头牛调用相同的函数时,它会发出哞哞的声音。
虽然我们只有一个函数,但它在不同的情况下表现不同。功能有多种形式;因此,我们实现了多态性。
在本 C++ 教程中,您将学习:
- 什么是多态性?
- 多态的类型
- 编译时多态性
- 函数重载
- 运算符重载
- 运行时多态性
- 函数重写
- 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; }
输出:
下面是代码截图:
代码说明:
- 将 iostream 头文件包含到我们的代码中。我们将能够使用它的功能。
- 在我们的代码中包含 std 命名空间。我们将能够在不调用它的情况下使用它的类。
- 创建一个名为 test 的函数,它接受一个整数参数 i。 { 标志着函数测试体的开始。
- 调用/调用上述函数测试时要执行的语句。
- 上述函数测试体结束。
- 创建一个名为 test 的函数,它接受一个浮点参数 f。 { 标志着函数测试体的开始。
- 调用/调用上述函数测试时要执行的语句。
- 上述函数测试的主体结束。
- 创建一个名为 test 的函数,它接受一个字符参数 ch。 { 标志着函数测试体的开始。
- 调用/调用上述函数测试时要执行的语句。
- 上述函数测试的主体结束。
- 调用 main() 函数。 { 标记函数体的开始。
- 调用函数 test 并将 5 作为参数的值传递给它。这将调用接受整数参数的测试函数,即第一个测试函数。
- 调用函数 test 并将 5.5 作为参数的值传递给它。这将调用接受浮点参数的测试函数,即第二个测试函数。
- 调用函数 test 并将 5 作为参数的值传递给它。这将调用接受字符参数的测试函数,即第三个测试函数。
- 如果程序成功运行,它必须返回一个值。
- main() 函数体的结尾。
我们有三个名称相同但参数类型不同的函数。我们已经实现了多态性。
运算符重载
在运算符重载中,我们为 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(); }
输出:
下面是代码截图:
代码说明:
- 将 iostream 头文件包含到我们的程序中,以便使用它的功能。
- 将 std 命名空间包含在我们的程序中,以便在不调用它的情况下使用它的类。
- 创建一个名为 ComplexNum 的类。 { 标志着类主体的开始。
- 使用 private 访问修饰符将变量标记为私有,这意味着它们只能从类内部访问。
- 定义两个整数变量,real 和 over。
- 使用 public 访问修饰符将构造函数标记为 public,这意味着即使从类外部也可以访问它。
- 创建类构造函数并初始化变量。
- 初始化变量real的值。
- 初始化过变量的值。
- 构造函数体结束。
- 我们需要重写 + 运算符的含义。
- 创建 ComplexNum 类型的数据类型结果。
- 对复数使用 + 运算符。此行会将一个数字的实部添加到另一个数字的实部。
- 对复数使用 + 运算符。这条线将一个数字的虚部添加到另一个数字的虚部。
- 程序执行成功后会返回变量result的值。
- 定义结束+运算符的新含义,即重载。
- 调用 print() 方法。
- 在控制台上打印添加后的新复数。
- print() 函数体结束。
- ComplexNum 类的主体结束。
- 调用 main() 函数。
- 传递要添加的实部和复部的值。 c1 的第一部分将添加到 c2 的第一部分,即 10+3。 c1的第二部分会加到c的第二部分,即2+7。
- 使用重载的 + 运算符执行操作,并将结果存储在变量 c3 中。
- 在控制台上打印变量 c3 的值。
- 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; }
输出:
下面是代码截图:
代码说明:
- 将 iostream 头文件导入到我们的程序中以使用它的功能。
- 将 std 命名空间包含在我们的程序中,以便在不调用它的情况下使用它的类。
- 创建一个名为 Mammal 的类。 { 标志着类主体的开始。
- 使用 public 访问修饰符将我们即将创建的函数设置为可公开访问。可以从此类外部访问它。
- 创建一个名为 eat 的公共函数。 { 标记函数体的开始。
- 打印调用函数eat() 时添加到cout 函数的语句。
- 函数体eat()的结束。
- 哺乳动物类主体的结尾。
- 创建一个名为 Cow 的类,它继承了 Mammal 类。 Cow 是派生类,而 Mammal 是基类。 { 标志着这个类的开始。
- 使用 public 访问修饰符将我们即将创建的函数标记为可公开访问。它将可以从这个类之外访问。
- 覆盖基类中定义的函数eat()。 { 标记函数体的开始。
- 调用此函数时在控制台上打印的语句。
- 函数体eat()的结束。
- Cow 类主体的结尾。
- 调用 main() 函数。 { 标记此函数主体的开始。
- 创建 Cow 类的一个实例,并将其命名为 c。
- 调用 Cow 类中定义的eat() 函数。
- 程序必须在成功完成后返回一个值。
- 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(); }
输出:
下面是代码截图:
代码说明:
- 在代码中包含 iostream 头文件以使用其功能。
- 在我们的代码中包含 std 命名空间以使用它的类而不调用它。
- 创建一个名为 ClassA 的类。
- 使用公共访问修饰符将类成员标记为可公开访问。
- 创建一个名为 show() 的虚函数。这将是一项公共职能。
- 调用 show() 时要打印的文本。 endl 是 C++ 关键字,表示结束行。它将鼠标光标移动到下一行。
- 虚函数 show() 的主体结束。
- ClassA 类主体的结尾。
- 创建一个名为 ClassB 的新类,该类继承类 ClassA。 ClassA 成为基类,而 ClassB 成为派生类。
- 使用公共访问修饰符将类成员标记为可公开访问。
- 重新定义基类中派生的虚函数show()。
- 调用派生类中定义的 show() 函数时在控制台上打印的文本。
- show() 函数主体的结尾。
- 派生类 ClassB 主体的结尾。
- 调用 main() 函数。程序逻辑应添加到其主体中。
- 创建一个名为a的指针变量。它指向名为 ClassA 的类。
- 创建一个名为 ClassB 的类的实例。该实例被命名为 b。
- 将值存储在变量 a 中的地址 b 中。
- 调用派生类中定义的 show() 函数。后期绑定已实施。
- main() 函数体的结尾。
编译时多态性对比。运行时多态
以下是两者之间的主要区别:
总结:
- 多态意味着有多种形式。
- 当存在通过继承相关的类层次结构时会发生这种情况。
- 使用多态性,函数可以根据调用/调用它的对象而有所不同。
- 在编译期多态中,要调用的函数是在编译期建立的。
- 在运行时多态中,要调用的函数是在运行时建立的。
- 编译时多态性是通过函数重载和运算符重载来确定的。
- 在函数重载中,有许多函数名称相似但参数不同。
- 参数的数量或类型可以不同。
- 在运算符重载中,为 C++ 运算符定义了新的含义。
- 运行时多态性是通过函数重写实现的。
- 在函数覆盖中,派生类为基类中定义的函数提供新定义。
C语言