C++ 多态性示例
什么是多态性 C++?
In C++多态性会导致成员函数根据调用它的对象而表现不同。多态性是一个希腊词,意思是有多种形式。当您拥有通过继承而关联的类层次结构时,就会发生这种情况。
例如,假设我们有函数 makeSound()。当猫调用此函数时,它会发出喵喵的声音。当牛调用同一函数时,它会发出哞哞的声音。
虽然我们有一个函数,但是它在不同情况下的行为却不同。该函数有多种形式;因此,我们实现了多态性。
多态性的类型
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 主体的开始。
- 如果上述函数测试被调用,则执行的语句。
- 以上功能测试主体结束。
- 创建一个名为 test 的函数,该函数采用浮点参数 f。{ 标记函数 test 主体的开始。
- 如果上述函数测试被调用,则执行的语句。
- 上述函数测试主体结束。
- 创建一个名为 test 的函数,该函数接受一个字符参数 ch。{ 标记函数 test 主体的开始。
- 如果上述函数测试被调用,则执行的语句。
- 上述函数测试主体结束。
- 调用 main() 函数。{ 标记函数主体的开始。
- 调用函数 test,并将 5 作为参数值传递给它。这将调用接受整数参数的测试函数,即第一个测试函数。
- 调用函数 test,并将 5.5 作为参数值传递给它。这将调用接受浮点参数的测试函数,即第二个测试函数。
- 调用函数 test,并将 5 作为参数值传递给它。这将调用接受字符参数的测试函数,即第三个测试函数。
- 如果程序运行成功,必须返回一个值。
- 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(); }
输出:
以下是代码截图:
代码说明:
- 将iostream头文件包含到我们的程序中才能使用其功能。
- 将 std 命名空间包含到我们的程序中,以便在不调用它的情况下使用它的类。
- 创建一个名为 ComplexNum 的类。{ 标记类主体的开始。
- 使用 private 访问修饰符将变量标记为私有,这意味着它们只能从类内部访问。
- 定义两个整数变量,real 和 over。
- 使用 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 的类。{ 标记类主体的开始。
- 使用公共访问修饰符将我们要创建的函数设置为可公开访问。它将可从此类外部访问。
- 创建一个名为 eat 的公共函数。{ 标记函数体的开始。
- 在调用函数eat()时打印添加到cout函数的语句。
- eat() 函数体结束。
- 哺乳动物类身体的结束。
- 创建一个名为 Cow 的类,该类继承自 Mammal 类。Cow 是派生类,而 Mammal 是基类。{ 标记此类的开始。
- 使用公共访问修饰符将我们要创建的函数标记为可公开访问。它将可从此类外部访问。
- 覆盖基类中定义的函数 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。
- 将地址b中存储的值赋给变量a。
- 调用派生类中定义的 show() 函数。已实现后期绑定。
- main() 函数体结束。
编译时多态性与运行时多态性
以下是两者之间的主要区别:
编译时多态 | 运行时多态性 |
---|---|
这也被称为早期绑定或静态多态性 | 这也被称为后期/动态绑定或动态多态性 |
该方法在编译时被调用 | 该方法在运行时被调用 |
通过函数重载和运算符重载实现 | 通过方法重写和虚函数实现 |
例如,方法重载。许多方法可能具有相似的名称,但参数的数量或类型不同 | 例如,方法覆盖。许多方法可能具有相似的名称和相同的原型。 |
由于方法发现是在编译时完成的,因此执行速度更快 | 由于方法发现是在运行时完成的,因此执行速度较慢。 |
Less 由于在编译时所有信息都是已知的,因此为解决问题提供了灵活性。 | 由于方法是在运行时发现的,因此为解决复杂问题提供了很大的灵活性。 |
总结
- 多态意味着具有多种形式。
- 当存在通过继承而相关的类的层次结构时,就会发生这种情况。
- 通过多态性,函数可以根据调用它的对象表现出不同的行为。
- 在编译时多态性中,要调用的函数是在编译时确定的。
- 在运行时多态中,要调用的函数是在运行时建立的。
- 编译时多态性是通过函数重载和运算符重载来确定的。
- 在函数重载中,有许多函数具有相似的名称,但参数不同。
- 参数的数量或类型可以不同。
- 在运算符重载中,为 C++ 运营商.
- 运行时多态性是通过函数重写实现的。
- 在函数覆盖中,派生类为基类中定义的函数赋予新的定义。