赞
踩
目录
(图像由AI生成)
多态是C++语言的三大特性之一,另两个特性是封装和继承。多态性使得对象可以根据运行时的实际类型来表现出不同的行为,从而实现灵活和可扩展的设计。在软件开发过程中,多态能够提高代码的复用性和可维护性,减少重复代码,并提供更加抽象和通用的接口。本文将详细探讨C++中的多态,包括其定义、构成、虚函数、抽象类以及实现原理,帮助读者全面理解这一重要概念。
多态(Polymorphism)是指同一接口在不同场景下可以表现出不同的行为。在面向对象编程中,多态允许程序在不同的上下文中调用相同的接口,从而处理不同的数据类型或对象。具体来说,多态可以分为两类:编译时多态(静态多态)和运行时多态(动态多态)。在C++中,编译时多态通过函数重载和模板实现,而运行时多态则通过虚函数和继承实现。本文主要讨论运行时多态。
在C++中,要实现多态,需要满足以下条件:
virtual
关键字)。示例代码
- #include <iostream>
- using namespace std;
-
- // 基类
- class Animal {
- public:
- // 声明虚函数
- virtual void makeSound() const {
- cout << "Animal makes a sound" << endl;
- }
- };
-
- // 派生类:Dog
- class Dog : public Animal {
- public:
- // 重写虚函数
- void makeSound() const override {
- cout << "Dog barks" << endl;
- }
- };
-
- // 派生类:Cat
- class Cat : public Animal {
- public:
- // 重写虚函数
- void makeSound() const override {
- cout << "Cat meows" << endl;
- }
- };
-
- int main() {
- // 创建对象
- Animal* animal1 = new Dog();
- Animal* animal2 = new Cat();
-
- // 通过基类指针调用虚函数
- animal1->makeSound(); // 输出:Dog barks
- animal2->makeSound(); // 输出:Cat meows
-
- // 释放内存
- delete animal1;
- delete animal2;
-
- return 0;
- }

在上面的代码中,Animal
类是基类,Dog
和Cat
类是派生类。基类中的makeSound
函数被声明为虚函数,派生类对该函数进行了重写。在main
函数中,通过基类指针调用虚函数,根据实际对象的类型,调用了不同的函数版本,实现了多态。这就是运行时多态的典型实现方式。
虚函数是使用 virtual
关键字声明的函数,用于实现运行时多态。在基类中声明虚函数时,派生类可以重写该函数。当通过基类指针或引用调用虚函数时,会根据实际对象的类型调用对应的函数版本,而不是基类的版本。虚函数允许派生类提供自己特有的实现,使得代码更加灵活和可扩展。
虚函数的重写是指在派生类中重新定义基类中的虚函数。重写虚函数时,派生类的函数签名必须与基类中的虚函数相同。通过这种方式,派生类可以提供其特有的行为。
示例代码
- #include <iostream>
- using namespace std;
-
- class Base {
- public:
- virtual void display() const {
- cout << "Display from Base class" << endl;
- }
- };
-
- class Derived : public Base {
- public:
- void display() const override {
- cout << "Display from Derived class" << endl;
- }
- };
-
- int main() {
- Base* basePtr = new Derived();
- basePtr->display(); // 输出:Display from Derived class
-
- delete basePtr;
- return 0;
- }

协变是指在派生类中重写虚函数时,允许返回类型是基类返回类型的派生类。这种特性使得派生类可以返回更具体的对象,而不违反函数重写的规则。
示例代码
- #include <iostream>
- using namespace std;
-
- class Base {
- public:
- virtual Base* clone() const {
- return new Base(*this);
- }
- virtual void display() const {
- cout << "Base" << endl;
- }
- };
-
- class Derived : public Base {
- public:
- Derived* clone() const override { // 协变返回类型
- return new Derived(*this);
- }
- void display() const override {
- cout << "Derived" << endl;
- }
- };
-
- int main() {
- Base* basePtr = new Derived();
- Base* newBasePtr = basePtr->clone(); // 返回Derived类的对象
- newBasePtr->display(); // 输出:Derived
-
- delete basePtr;
- delete newBasePtr;
- return 0;
- }

在这个示例中,Derived
类重写了clone
函数,并返回类型是Derived*
,而不是基类的Base*
,这就是协变的应用。
在使用继承和多态时,基类的析构函数应该声明为虚函数,以确保派生类对象被正确销毁。这是因为如果析构函数不是虚函数,通过基类指针删除派生类对象时,只会调用基类的析构函数,导致资源泄漏。
示例代码
- #include <iostream>
- using namespace std;
-
- class Base {
- public:
- virtual ~Base() { // 基类析构函数声明为虚函数
- cout << "Base destructor" << endl;
- }
- };
-
- class Derived : public Base {
- public:
- ~Derived() override {
- cout << "Derived destructor" << endl;
- }
- };
-
- int main() {
- Base* basePtr = new Derived();
- delete basePtr; // 正确调用Derived和Base的析构函数
-
- return 0;
- }

在这个示例中,基类Base
的析构函数被声明为虚函数,因此通过基类指针删除派生类对象时,会正确调用派生类的析构函数,避免资源泄漏。
override
关键字用于显式声明派生类中的虚函数是重写基类中的虚函数。它有助于编译器进行检查,以确保派生类中的函数确实是在重写基类中的虚函数,而不是定义一个新的函数。这不仅提高了代码的可读性,还减少了因拼写错误或参数不匹配导致的隐藏错误。
- #include <iostream>
- using namespace std;
-
- class Base {
- public:
- virtual void display() const {
- cout << "Display from Base class" << endl;
- }
- };
-
- class Derived : public Base {
- public:
- void display() const override { // 使用override关键字
- cout << "Display from Derived class" << endl;
- }
- };
-
- int main() {
- Base* basePtr = new Derived();
- basePtr->display(); // 输出:Display from Derived class
-
- delete basePtr;
- return 0;
- }

在这个示例中,Derived
类中的display
函数使用了override
关键字,明确指出这是对基类中display
函数的重写。
final
关键字用于防止虚函数在派生类中再次被重写。它可以用于虚函数的声明中,表示该函数不能在进一步派生的类中被重写。另外,final
关键字还可以用于类声明,表示该类不能被继承。
- #include <iostream>
- using namespace std;
-
- class Base {
- public:
- virtual void display() const {
- cout << "Display from Base class" << endl;
- }
- };
-
- class Derived final : public Base { // 使用final关键字禁止进一步继承
- public:
- void display() const override {
- cout << "Display from Derived class" << endl;
- }
- };
-
- /*
- class FurtherDerived : public Derived { // 错误!Derived类被final修饰,不能被继承
- };
- */
-
- class AnotherBase {
- public:
- virtual void show() const final { // 使用final关键字禁止重写
- cout << "Show from AnotherBase class" << endl;
- }
- };
-
- class AnotherDerived : public AnotherBase {
- /*
- void show() const override { // 错误!show函数被final修饰,不能被重写
- cout << "Show from AnotherDerived class" << endl;
- }
- */
- };
-
- int main() {
- Base* basePtr = new Derived();
- basePtr->display(); // 输出:Display from Derived class
-
- delete basePtr;
- return 0;
- }

在这个示例中,Derived
类被声明为final
,表示不能再派生新的类。同时,AnotherBase
类中的show
函数被声明为final
,表示不能在派生类中被重写。
重载是指在同一个作用域中,函数名相同但参数列表不同的多个函数。重载函数可以有不同的参数类型、数量或顺序,但不能仅靠返回类型区分。
- #include <iostream>
- using namespace std;
-
- class Example {
- public:
- void func(int x) {
- cout << "Function with int parameter: " << x << endl;
- }
-
- void func(double x) {
- cout << "Function with double parameter: " << x << endl;
- }
-
- void func(int x, double y) {
- cout << "Function with int and double parameters: " << x << ", " << y << endl;
- }
- };
-
- int main() {
- Example ex;
- ex.func(5); // 输出:Function with int parameter: 5
- ex.func(5.5); // 输出:Function with double parameter: 5.5
- ex.func(5, 5.5); // 输出:Function with int and double parameters: 5, 5.5
-
- return 0;
- }

在这个示例中,func
函数被重载了三次,分别接受不同的参数列表。
重写是指在派生类中重新定义基类中的虚函数。重写函数的签名必须与基类中的虚函数一致。通过重写,派生类可以提供特定的实现。
- #include <iostream>
- using namespace std;
-
- class Base {
- public:
- virtual void display() const {
- cout << "Display from Base class" << endl;
- }
- };
-
- class Derived : public Base {
- public:
- void display() const override { // 重写基类的虚函数
- cout << "Display from Derived class" << endl;
- }
- };
-
- int main() {
- Base* basePtr = new Derived();
- basePtr->display(); // 输出:Display from Derived class
-
- delete basePtr;
- return 0;
- }

在这个示例中,Derived
类重写了Base
类中的虚函数display
。
重定义(隐藏)是指在派生类中定义了与基类同名但参数列表不同的函数。这种情况下,基类的同名函数在派生类中会被隐藏,但它们并不是重写,因此不会发生多态。
- #include <iostream>
- using namespace std;
-
- class Base {
- public:
- void show() const {
- cout << "Show from Base class" << endl;
- }
- };
-
- class Derived : public Base {
- public:
- void show(int x) const { // 重定义基类的show函数
- cout << "Show from Derived class with parameter: " << x << endl;
- }
- };
-
- int main() {
- Derived derived;
- derived.show(5); // 输出:Show from Derived class with parameter: 5
- // derived.show(); // 错误!没有与之匹配的show函数
-
- Base* basePtr = &derived;
- basePtr->show(); // 输出:Show from Base class
-
- return 0;
- }

在这个示例中,Derived
类中的show
函数隐藏了Base
类中的同名函数。通过派生类对象只能调用重定义后的函数,通过基类指针调用时则调用基类的函数。
抽象类是不能实例化的类,通常作为其他类的基类使用。它包含至少一个纯虚函数。纯虚函数是没有具体实现的函数,只提供接口规范。纯虚函数使用=0
语法声明,表示派生类必须重写该函数。抽象类用于定义一个统一的接口,让派生类去实现具体的行为,从而实现多态。
- #include <iostream>
- using namespace std;
-
- class AbstractClass {
- public:
- virtual void pureVirtualFunction() const = 0; // 纯虚函数
- void concreteFunction() const {
- cout << "Concrete function in AbstractClass" << endl;
- }
- };
-
- class ConcreteClass : public AbstractClass {
- public:
- void pureVirtualFunction() const override {
- cout << "Implementation of pureVirtualFunction in ConcreteClass" << endl;
- }
- };
-
- int main() {
- // AbstractClass obj; // 错误!不能实例化抽象类
- ConcreteClass obj;
- obj.pureVirtualFunction(); // 输出:Implementation of pureVirtualFunction in ConcreteClass
- obj.concreteFunction(); // 输出:Concrete function in AbstractClass
-
- return 0;
- }

在这个示例中,AbstractClass
是一个抽象类,包含一个纯虚函数pureVirtualFunction
。ConcreteClass
是AbstractClass
的派生类,提供了pureVirtualFunction
的实现。
接口继承是指派生类继承抽象类的函数声明,而不继承其具体实现。派生类必须提供所有纯虚函数的具体实现。接口继承使得派生类可以有不同的实现,但遵循相同的接口规范。
- #include <iostream>
- using namespace std;
-
- class Interface {
- public:
- virtual void doSomething() const = 0; // 纯虚函数,接口声明
- };
-
- class ImplementationA : public Interface {
- public:
- void doSomething() const override {
- cout << "Implementation A doing something" << endl;
- }
- };
-
- class ImplementationB : public Interface {
- public:
- void doSomething() const override {
- cout << "Implementation B doing something" << endl;
- }
- };
-
- int main() {
- Interface* a = new ImplementationA();
- Interface* b = new ImplementationB();
-
- a->doSomething(); // 输出:Implementation A doing something
- b->doSomething(); // 输出:Implementation B doing something
-
- delete a;
- delete b;
-
- return 0;
- }

在这个示例中,Interface
类是一个抽象类,定义了一个纯虚函数doSomething
。ImplementationA
和ImplementationB
类分别提供了该函数的具体实现,实现了接口继承。
实现继承是指派生类不仅继承基类的接口,还继承其具体实现。基类中的非纯虚函数可以在派生类中直接使用,也可以在派生类中被重写。实现继承使得派生类可以复用基类的代码,减少重复实现。
- #include <iostream>
- using namespace std;
-
- class BaseClass {
- public:
- virtual void virtualFunction() const {
- cout << "BaseClass implementation of virtualFunction" << endl;
- }
- void anotherFunction() const {
- cout << "BaseClass implementation of anotherFunction" << endl;
- }
- };
-
- class DerivedClass : public BaseClass {
- public:
- void virtualFunction() const override {
- cout << "DerivedClass override of virtualFunction" << endl;
- }
- };
-
- int main() {
- DerivedClass obj;
- obj.virtualFunction(); // 输出:DerivedClass override of virtualFunction
- obj.anotherFunction(); // 输出:BaseClass implementation of anotherFunction
-
- return 0;
- }

在这个示例中,BaseClass
包含一个虚函数virtualFunction
和一个普通成员函数anotherFunction
。DerivedClass
重写了virtualFunction
,但直接继承并使用了anotherFunction
的实现。这就是实现继承的应用。
虚函数表(Virtual Table,简称vtable)是实现C++多态的核心机制。当一个类包含虚函数时,编译器会为该类生成一个虚函数表。虚函数表是一个指针数组,每个元素指向该类的一个虚函数。每个对象在创建时都会包含一个指向虚函数表的指针(vptr),通过这个指针,程序在运行时能够找到并调用对象实际类型的虚函数。
- #include <iostream>
- using namespace std;
-
- class Base {
- public:
- virtual void func1() { cout << "Base func1" << endl; }
- virtual void func2() { cout << "Base func2" << endl; }
- };
-
- class Derived : public Base {
- public:
- void func1() override { cout << "Derived func1" << endl; }
- void func2() override { cout << "Derived func2" << endl; }
- };
-
- int main() {
- Base* basePtr = new Derived();
- basePtr->func1(); // 输出:Derived func1
- basePtr->func2(); // 输出:Derived func2
-
- delete basePtr;
- return 0;
- }

在这个示例中,Base
类和Derived
类都有两个虚函数func1
和func2
。Base
类的虚函数表会指向其虚函数实现,而Derived
类的虚函数表会指向其重写的虚函数实现。通过基类指针调用虚函数时,会通过vptr找到实际对象的虚函数表,从而调用正确的函数版本。
多态的实现原理依赖于虚函数表和虚函数表指针。以下是多态实现的几个关键步骤:
- #include <iostream>
- using namespace std;
-
- class Animal {
- public:
- virtual void speak() { cout << "Animal speaks" << endl; }
- };
-
- class Dog : public Animal {
- public:
- void speak() override { cout << "Dog barks" << endl; }
- };
-
- class Cat : public Animal {
- public:
- void speak() override { cout << "Cat meows" << endl; }
- };
-
- void makeAnimalSpeak(Animal& animal) {
- animal.speak();
- }
-
- int main() {
- Dog dog;
- Cat cat;
- makeAnimalSpeak(dog); // 输出:Dog barks
- makeAnimalSpeak(cat); // 输出:Cat meows
-
- return 0;
- }

在这个示例中,makeAnimalSpeak
函数接受一个Animal
类的引用参数,通过该引用调用虚函数speak
。实际调用的是Dog
和Cat
类重写的speak
函数,从而实现多态。
静态绑定(Static Binding):静态绑定在编译时进行决策,函数调用在编译时被解析。普通成员函数和非虚函数使用静态绑定。静态绑定的优点是速度快,因为在编译时已经确定了调用地址。
动态绑定(Dynamic Binding):动态绑定在运行时进行决策,函数调用在运行时通过虚函数表解析。虚函数使用动态绑定。动态绑定的优点是灵活,可以在运行时根据对象的实际类型调用相应的函数版本。
静态绑定示例代码
- #include <iostream>
- using namespace std;
-
- class Base {
- public:
- void staticFunc() { cout << "Base staticFunc" << endl; }
- };
-
- class Derived : public Base {
- public:
- void staticFunc() { cout << "Derived staticFunc" << endl; }
- };
-
- int main() {
- Base base;
- Derived derived;
- Base* basePtr = &derived;
-
- base.staticFunc(); // 输出:Base staticFunc
- derived.staticFunc(); // 输出:Derived staticFunc
- basePtr->staticFunc(); // 输出:Base staticFunc
-
- return 0;
- }

在这个示例中,staticFunc
是普通成员函数,不是虚函数,因此使用静态绑定。即使通过基类指针调用staticFunc
,也调用的是基类版本。
动态绑定示例代码
- #include <iostream>
- using namespace std;
-
- class Base {
- public:
- virtual void dynamicFunc() { cout << "Base dynamicFunc" << endl; }
- };
-
- class Derived : public Base {
- public:
- void dynamicFunc() override { cout << "Derived dynamicFunc" << endl; }
- };
-
- int main() {
- Base base;
- Derived derived;
- Base* basePtr = &derived;
-
- base.dynamicFunc(); // 输出:Base dynamicFunc
- derived.dynamicFunc(); // 输出:Derived dynamicFunc
- basePtr->dynamicFunc(); // 输出:Derived dynamicFunc
-
- return 0;
- }

在这个示例中,dynamicFunc
是虚函数,因此使用动态绑定。通过基类指针调用dynamicFunc
时,会根据实际对象的类型调用Derived
类的版本。
通过深入探讨C++中的多态特性及其实现原理,我们可以理解虚函数、虚函数表以及静态绑定和动态绑定的机制。多态作为C++的三大特性之一,不仅提升了代码的灵活性和可扩展性,还提高了程序设计的抽象能力。掌握多态的概念和应用,对于编写高质量的面向对象程序至关重要。希望本文能够帮助读者更好地理解和运用C++中的多态特性。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。