C++虚函数表失效???

最近写c++代码,发现一个和java不一样的地方,就是在构造函数中,调用虚函数,似乎C++的虚函数表没生效:

参考如下代码:

c++ 复制代码
#include <iostream>

class Base {
public:
    Base() {
        A();  // 调用虚函数
    }

    virtual void A() {
        std::cout << "Base::A()" << std::endl;
    }
};

class Derived : public Base {
public:
    Derived() {}

    void A() override {
        std::cout << "Derived::A()" << std::endl;
    }
};

int main() {
    Derived d;
}

//输出 Base::A()

而java:

java 复制代码
class Base {
    Base() {
        print(); // 调用子类重写的方法
    }
    void print() {
        System.out.println("Base");
    }
}

class Derived extends Base {
    private int value = 10;

    @Override
    void print() {
        System.out.println("Derived: " + value);
    }
}

public class Main {
    public static void main(String[] args) {
        Derived d = new Derived(); // 输出:Derived: 0
    }
}

深入研究发现区别在于:

  • Java :对象在构造前就已分配内存,包括所有成员变量(已设置默认值 ,但是未初始化)

    Java 的对象模型是统一的:Java 的对象从创建的第一刻起就知道它们的最终类型。在 Java 中,当你创建一个派生类对象时,虚表(vtable)已经绑定到派生类的实现。因此,哪怕对象还在构造中,调用的方法都会是派生类重写的方法。

  • C++:对象逐步构造,未构造部分包含未初始化的内存

    在 C++ 中,构造过程从基类到派生类逐步进行。在基类的构造阶段,派生类的部分成员可能还未初始化,因此动态多态机制(虚表)还没有完全就绪。此时,调用虚函数会调用基类版本,而不是派生类的重写版本。

C++ vptr的初始化时机

复制代码
class MyClass : public Base1, public Base2 {
    // 构造函数调用顺序:
    // 1. Base1构造函数(vptr = Base1的vtable)
    // 2. Base2构造函数(vptr = Base2的vtable)
    // 3. MyClass构造函数(vptr = MyClass的vtable)
};

构造过程中,对象处于"半构造"状态:

  • 基类部分已构造完成
  • 派生类部分尚未构造
  • vptr需要逐步更新到正确的vtable

所以C++ 在构造函数中调用虚函数无法生效

java机制的合理吗?

可以看到上面的例子里面,输出是 :Derived: 0 而不 Derived: 10 就说明问题了。

java对象在构造前就已分配内存,包括所有成员变量(已设置默认值 ,但是未初始化)

子类方法可能依赖未初始化的字段可能导致不可预测的行为,导致调试困难,所以最好也不要这样使用。

《Effective Java》第19条也明确明确建议:

"在构造函数中不要调用可被覆盖的方法,无论是直接还是间接调用。"

相关推荐
艾iYYY5 分钟前
思考: C++ 万能引用与完美转发
c++·windows·microsoft
IT研究室5 分钟前
最新计算机毕业设计选题推荐-基于spring boot的教育学习平台-网站-文档指导-Java-springboot
java·spring boot·课程设计
余额瞒着我当琳6 分钟前
C++算法学习篇5--二分查找法
c++·学习·算法
亚林瓜子7 分钟前
Spring Boot 集成 ip2region 离线 IP 库实战指南
java·spring boot·后端·ip·ip2region
Want59515 分钟前
Python太极八卦图
开发语言·python
饕餮争锋26 分钟前
python开发工具uv简介
开发语言·python·uv
盛世宏博智慧档案34 分钟前
恒湿净化一体机联动开发:档案馆温湿度阈值判断与启停控制实践
java·后端·struts·恒湿机·加湿除湿消毒净化一体机
蒸蒸yyyyzwd35 分钟前
秋招学习笔记 Day 64 之准备系统软件开发工程师岗位笔记
c++·八股
奇树谦36 分钟前
LRU 缓存原理与 C++ 实践示例
开发语言·c++·缓存
toooooop838 分钟前
FastAdmin Hook 机制与插件错误透传:`$result` 之外的信封
开发语言·fastadmin