C++多态全面解析:从概念到实现

1. 多态的基本概念

1.1 什么是多态?

多态(polymorphism)是面向对象编程的三大特性之一,指同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。

两种多态类型:

  • 编译时多态(静态多态):函数重载、模板

  • 运行时多态(动态多态):通过虚函数和继承实现

1.2 实际应用场景

  • 买票行为:普通人全价,学生半价,军人优先

  • 动物叫声:猫"喵喵",狗"汪汪"

  • 交通工具:汽车行驶,飞机飞行

2. 多态的实现条件

2.1 必须满足的条件

cpp

复制代码
class Person {
public:
    virtual void BuyTicket() {  // 1. 必须是虚函数
        cout << "买票-全价" << endl;
    }
};

class Student : public Person {
public:
    virtual void BuyTicket() override {  // 2. 派生类必须重写虚函数
        cout << "买票-半价" << endl;
    }
};

void Func(Person& people) {  // 3. 必须是基类的指针或引用
    people.BuyTicket();  // 4. 调用虚函数
}

2.2 为什么必须是基类指针/引用?

基类指针/引用可以:

  1. 指向基类对象

  2. 指向派生类对象(向上转型)

  3. 实现统一接口,不同实现

3. 虚函数详解

3.1 虚函数声明

cpp

复制代码
class Base {
public:
    virtual void func();  // 虚函数声明
};

void Base::func() {      // 类外定义不加virtual
    // 实现
}

3.2 虚函数重写规则

  1. 函数签名必须完全相同(返回值类型、函数名、参数列表)

  2. 基类函数必须有virtual关键字

  3. 派生类virtual可省略(但不推荐)

3.3 协变(特殊情况)

cpp

复制代码
class A {};
class B : public A {};

class Base {
public:
    virtual A* create() { return new A; }
};

class Derived : public Base {
public:
    virtual B* create() override { return new B; }  // 协变:返回值类型不同
};

4. 虚函数表原理

4.1 虚函数表结构

cpp

复制代码
class Base {
public:
    virtual void func1() {}
    virtual void func2() {}
    int a;
};

int main() {
    Base b;
    cout << sizeof(b) << endl;  // 输出:8(32位)或16(64位)
    // 包含:虚表指针 + 成员变量
    return 0;
}

4.2 内存布局

text

复制代码
Base对象:
+--------------+
| vptr         | -> 指向虚函数表
+--------------+
| int a        | -> 成员变量
+--------------+

虚函数表:
+--------------+
| &Base::func1 |
+--------------+
| &Base::func2 |
+--------------+

4.3 派生类虚表

cpp

复制代码
class Derived : public Base {
public:
    virtual void func1() override {}  // 重写
    virtual void func3() {}           // 新增虚函数
};

// Derived虚表:
// 1. &Derived::func1(覆盖基类)
// 2. &Base::func2(继承)
// 3. &Derived::func3(新增)

5. 重要特例

5.1 虚析构函数

cpp

复制代码
class Base {
public:
    virtual ~Base() {  // 虚析构函数
        cout << "~Base()" << endl;
    }
};

class Derived : public Base {
public:
    ~Derived() {  // 自动成为虚函数
        cout << "~Derived()" << endl;
    }
};

int main() {
    Base* ptr = new Derived();
    delete ptr;  // 正确调用~Derived()和~Base()
    return 0;
}

5.2 纯虚函数与抽象类

cpp

复制代码
class Animal {  // 抽象类
public:
    virtual void makeSound() = 0;  // 纯虚函数
    
    // 纯虚函数可以有实现(但很少用)
    virtual void sleep() = 0 { 
        cout << "Animal sleeping" << endl;
    }
};

class Dog : public Animal {
public:
    virtual void makeSound() override {
        cout << "汪汪" << endl;
    }
    
    virtual void sleep() override {
        Animal::sleep();  // 调用基类实现
        cout << "Dog dreaming" << endl;
    }
};

6. C++11新特性

6.1 override关键字

cpp

复制代码
class Base {
public:
    virtual void func(int) {}
};

class Derived : public Base {
public:
    virtual void func(int) override {}  // 正确
    // virtual void func(double) override {}  // 错误:不是重写
};

6.2 final关键字

cpp

复制代码
class Base {
public:
    virtual void func() final {}  // 禁止重写
};

class Derived : public Base {
public:
    // virtual void func() {}  // 错误:不能重写final函数
};

class Base2 final {};  // 禁止继承
// class Derived2 : public Base2 {};  // 错误

7. 重载、重写、隐藏对比

特性 重载 (Overload) 重写/覆盖 (Override) 隐藏 (Hide)
作用域 同一作用域 不同作用域(继承) 不同作用域(继承)
函数名 相同 相同 相同
参数列表 必须不同 必须相同 可以不同
返回值 可以不同 必须相同(协变除外) 可以不同
virtual 不需要 基类必须有virtual 不需要
访问权限 可以不同 可以不同 可以不同

8. 常见面试题

8.1 选择题示例

cpp

复制代码
class A {
public:
    virtual void func(int val = 1) { 
        cout << "A->" << val << endl;
    }
    virtual void test() { func(); }
};

class B : public A {
public:
    void func(int val = 0) { 
        cout << "B->" << val << endl;
    }
};

int main() {
    B* p = new B;
    p->test();  // 输出:B->1
    return 0;
}

解析 :调用test()时,this指向B对象,调用func()使用动态绑定。但默认参数使用静态绑定,所以使用A的默认值1。

8.2 内存布局题

cpp

复制代码
class Base {
public:
    virtual void f1() {}
    virtual void f2() {}
    int a;
    char b;
};

int main() {
    Base b;
    cout << sizeof(b) << endl;
    // 32位:4(vptr) + 4(int) + 1(char) + 3(对齐) = 12
    // 64位:8(vptr) + 4(int) + 1(char) + 3(对齐) = 16
    return 0;
}

8.3 多态原理题

cpp

复制代码
class Animal {
public:
    virtual void speak() const {
        cout << "Animal sound" << endl;
    }
};

class Cat : public Animal {
public:
    virtual void speak() const override {
        cout << "Meow" << endl;
    }
};

void makeSound(const Animal& animal) {
    animal.speak();  // 动态绑定
}

int main() {
    Cat cat;
    makeSound(cat);  // 输出:Meow
    return 0;
}

9. 最佳实践

9.1 何时使用多态?

  1. 需要处理多种类型对象,但有统一接口

  2. 需要在运行时决定调用哪个函数

  3. 需要扩展性,添加新类型不影响现有代码

9.2 设计建议

  1. 基类析构函数设为虚函数

  2. 使用override明确重写意图

  3. 接口类使用纯虚函数

  4. 避免在构造函数/析构函数中调用虚函数

9.3 性能考虑

  1. 虚函数调用有额外开销(虚表查找)

  2. 虚函数不能内联(通常)

  3. 虚表增加对象大小(一个指针)

10. 总结

多态是C++面向对象编程的核心特性,通过虚函数和继承实现运行时多态。理解多态需要掌握:

  1. 实现条件:虚函数 + 重写 + 基类指针/引用

  2. 底层原理:虚函数表和虚表指针

  3. 关键语法virtualoverridefinal

  4. 特殊场景:虚析构函数、纯虚函数、协变

  5. 设计原则:开闭原则、里氏替换原则

多态使得代码更加灵活、可扩展,是设计复杂系统的重要工具。正确使用多态可以提高代码的复用性和可维护性,但也需要注意性能开销和正确性问题。

相关推荐
十五年专注C++开发2 小时前
QProcess在Windows下不能正常启动exe的原因分析
开发语言·c++·windows·qprocess·createprocess
.似水2 小时前
Python面向对象
开发语言·python
无限进步_2 小时前
C++ STL容器适配器深度解析:stack、queue与priority_queue
开发语言·c++·ide·windows·算法·github·visual studio
山土成旧客2 小时前
【Python学习打卡-Day30】模块化编程:从“单兵作战”到“军团指挥”
开发语言·python·学习
lingzhilab2 小时前
零知IDE——零知ESP32-S3部署 AI小智 2.0版发布!调整界面UI,新增LED、舵机和风扇外部设备和控制
ide·交互
世转神风-2 小时前
qt-union-联合体基础讲解
开发语言·qt
moxiaoran57532 小时前
Go语言的数据类型转换
开发语言·后端·golang
秋邱2 小时前
Java包装类:基本类型与包装类转换、自动装箱与拆箱原理
java·开发语言·python
海上彼尚2 小时前
Go之路 - 8.go的接口
开发语言·golang·xcode