You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

构造函数中未调用重写的Setup函数问题咨询

为什么派生类的Setup函数在基类构造时未被调用?

这是C++的虚函数调用规则导致的:当基类构造函数执行时,派生类的成员变量还未完成初始化,此时对象的动态类型仅为基类类型。因此,调用虚函数时只会解析到基类的版本,不会触发派生类的重写函数——这是语言层面的保护机制,防止访问未初始化的派生类成员导致未定义行为。

在你的代码中,创建Derived对象时,执行顺序是:

  1. 进入Derived构造函数,首先调用Base的构造函数
  2. Base构造函数内调用Setup(),此时对象的动态类型是Base,因此调用的是Base::Setup(),将val_设为1
  3. Base构造完成后,才会执行Derived的构造逻辑(此处为空)
  4. 最终val_的值是1,所以Print()输出1

解决方案

方案1:使用工厂函数(推荐)

通过静态工厂函数创建对象,确保对象完全构造后再调用初始化函数:

#include <memory>
#include <iostream>

class Base
{
public:
    const int& Value() const {return val_;}

    void Print() const
    {
        std::cout << "My value is " << Value() << std::endl;
    }

    // 工厂函数,创建Base对象并完成初始化
    static std::unique_ptr<Base> Create()
    {
        auto obj = std::make_unique<Base>();
        obj->Setup();
        return obj;
    }

protected:
    // 构造函数设为protected,强制通过工厂函数创建
    Base() = default;

    void SetValue(double v) {val_ = v;}
    virtual void Setup() {SetValue(1.0);}

private:
    int val_;
};

class Derived : public Base
{
public:
    // 派生类的工厂函数
    static std::unique_ptr<Derived> Create()
    {
        auto obj = std::make_unique<Derived>();
        obj->Setup();
        return obj;
    }

protected:
    Derived() = default;
    void Setup() override {SetValue(2.0);}
};

// 使用示例
int main()
{
    auto a = Derived::Create();
    a->Print(); // 输出My value is 2
    return 0;
}

方案2:传递初始化参数给基类构造

如果Setup仅用于设置初始值,可以直接将初始值作为参数传递给基类构造函数,避免虚函数调用:

#include <iostream>

class Base
{
public:
    // 显式构造函数,接收初始值
    explicit Base(int initial_val = 1) : val_(initial_val) {}

    const int& Value() const {return val_;}

    void Print() const
    {
        std::cout << "My value is " << Value() << std::endl;
    }

protected:
    void SetValue(double v) {val_ = v;}
    // 若无需其他初始化逻辑,可去掉Setup函数

private:
    int val_;
};

class Derived : public Base
{
public:
    // 传递初始值2给基类构造
    Derived() : Base(2) {}
};

// 使用示例
int main()
{
    Derived a;
    a.Print(); // 输出My value is 2
    return 0;
}

方案3:两阶段初始化

明确区分对象构造和初始化步骤,构造完成后手动调用初始化函数:

#include <iostream>

class Base
{
public:
    Base() = default;

    // 手动调用的初始化函数
    void Initialize()
    {
        Setup();
    }

    const int& Value() const {return val_;}

    void Print() const
    {
        std::cout << "My value is " << Value() << std::endl;
    }

protected:
    void SetValue(double v) {val_ = v;}
    virtual void Setup() {SetValue(1.0);}

private:
    int val_;
};

class Derived : public Base
{
protected:
    void Setup() override {SetValue(2.0);}
};

// 使用示例
int main()
{
    Derived a;
    a.Initialize(); // 必须手动调用初始化
    a.Print(); // 输出My value is 2
    return 0;
}

内容的提问来源于stack exchange,提问作者kstn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 13:27:54