C++ virtual(虚函数)

C++ virtual(虚函数)

虚函数和虚函数表(vtable)是 C++ 中实现多态性的核心机制。它们允许在运行时根据对象的实际类型调用正确的虚函数实现,从而支持动态绑定和多态性。以下是对虚函数和虚函数表的详细解释:

1. 虚函数

  • 虚函数 是在基类中声明的函数,并且在派生类中可以被重写(override)。它的声明通过 virtual 关键字标记。
  • 目的:允许在运行时根据对象的实际类型调用正确的函数实现,从而支持多态性。

示例

#include <iostream>
using namespace std;

class Base {
public:
    virtual void foo() { cout << "Base::foo" << endl; }
};

class Derived : public Base {
public:
    void foo() override { cout << "Derived::foo" << endl; }
};

int main() {
    Base* b = new Derived();
    b->foo(); // Output: Derived::foo
    delete b;
    return 0;
}

在上面的示例中,尽管 b 的类型是 Base*,它实际指向一个 Derived 对象。调用 b->foo() 时,程序会根据 Derived 类的实现来调用 foo 函数,这就是通过虚函数实现的多态性。

2. 虚函数表(vtable)

  • 虚函数表 是一个由函数指针组成的表格,用于存储类的虚函数的地址。每个含有虚函数的类都有一个虚函数表。
  • 虚函数表的作用:支持动态绑定,确保在运行时调用正确的虚函数实现。

虚函数表的组成

  1. 每个虚函数表条目
    • 存储指向虚函数的指针。当虚函数被调用时,通过这些指针来找到并调用正确的函数实现。
  2. 虚函数表指针(vptr)
    • 对象中包含一个指针,称为 vptr,它指向该对象所属类的虚函数表。每个对象在创建时会被初始化其 vptr

示例

class A {
public:
    virtual void foo() { cout << "A::foo" << endl; }
    virtual void bar() { cout << "A::bar" << endl; }
};

class B : public A {
public:
    void foo() override { cout << "B::foo" << endl; }
};

int main() {
    B b;
    A* a = &b;
    a->foo(); // Output: B::foo
    a->bar(); // Output: A::bar
    return 0;
}

在这个例子中:

  • A 类有两个虚函数 foobar
  • B 类重写了 foo 函数。
  • B 类对象的虚函数表只包含 B::fooA::bar 的指针。
  • A* a = &b; 通过 a 调用 foobar 时,会通过 B 的虚函数表来找到 B::fooA::bar 的实现。

3. 内存布局

  • 对象内存布局:每个对象在内存中通常包含一个指向虚函数表的指针(vptr),vptr 指向虚函数表。虚函数表中包含指向虚函数实现的指针。
  • 虚函数表内存布局
    • 虚函数表的每个条目通常是一个函数指针,指向虚函数的实际实现。

4. 虚函数表的工作原理

  1. 类的虚函数表生成
    • 编译器为每个包含虚函数的类生成一个虚函数表,并填充虚函数的地址。
  2. 对象创建
    • 当创建一个对象时,编译器将该对象的 vptr 初始化为指向类的虚函数表。
  3. 函数调用
    • 当通过基类指针或引用调用虚函数时,程序通过 vptr 查找虚函数表,并调用虚函数表中对应的函数实现。

5. 多重继承和虚函数表

  • 多重继承 时,每个基类有一个虚函数表。子类对象包含多个 vptr,每个 vptr 指向一个基类的虚函数表。
  • 虚函数表的合并
    • 在多重继承中,编译器会处理虚函数表的合并,以确保每个基类的虚函数表能够正确地映射到子类对象的 vptr 上。

总结

  • 虚函数 是支持 C++ 多态性的关键,通过 virtual 关键字声明。
  • 虚函数表 存储虚函数的地址,支持动态绑定和多态性。
  • 虚函数表指针(vptr) 是对象的一部分,指向虚函数表,确保运行时调用正确的函数实现。
  • 多重继承 中,子类对象会维护多个 vptr,每个指向不同基类的虚函数表。

虚函数指针初始化时机,虚函数指针在构造函数开始时就会初始化。在构造函数中调用虚函数是没有意义的因为构造函数会从父类到子类层层递进构造,虚函数指针指向也会不断修改。在构造函数中调用虚函数并不能实现多态。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇