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. 设计原则:开闭原则、里氏替换原则

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

相关推荐
毕设源码-邱学长1 小时前
【开题答辩全过程】以 基于Java的学校住宿管理系统的设计与实现为例,包含答辩的问题和答案
java·开发语言
rookieﻬ°2 小时前
PHP框架漏洞
开发语言·php
NocoBase2 小时前
【2.0 教程】第 1 章:认识 NocoBase ,5 分钟跑起来
数据库·人工智能·开源·github·无代码
炸膛坦客3 小时前
单片机/C/C++八股:(二十)指针常量和常量指针
c语言·开发语言·c++
兑生3 小时前
【灵神题单·贪心】1481. 不同整数的最少数目 | 频率排序贪心 | Java
java·开发语言
炸膛坦客4 小时前
单片机/C/C++八股:(十九)栈和堆的区别?
c语言·开发语言·c++
零雲4 小时前
java面试:了解抽象类与接口么?讲一讲它们的区别
java·开发语言·面试
Jay_Franklin4 小时前
Quarto与Python集成使用
开发语言·python·markdown
2401_831824965 小时前
代码性能剖析工具
开发语言·c++·算法
Oueii5 小时前
Django全栈开发入门:构建一个博客系统
jvm·数据库·python