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

如何仅定义一次基类方法,使子类调用时使用子类成员而非基类?

问题描述

我定义了如下基类:

typedef float Pounds;
typedef int Seconds;
typedef float MilliJoulesPerLiter;
typedef float MilliJoules;
typedef int Liters;

class Fuel {
    protected:
        static const Pounds carbonDioxideEmmissionsPerLiter;
        static const Seconds burnTime;
        static const MilliJoulesPerLiter energyYield; 
        Liters volume {10};
        MilliJoules totalMilliJoules {0};
        
    public:
        Fuel() : Fuel {10} {};
        
        Fuel(int vol) : volume(vol) {
            totalMilliJoules = volume * energyYield;
        }

        virtual void burn() {
            cout << "Burning fuel..." << endl;
            int quantity = volume < burnTime ? volume : burnTime;
            Sleep(quantity);
            volume -= quantity;
            totalMilliJoules /= volume * energyYield;
            totalEmmissions += carbonDioxideEmmissionsPerLiter * quantity;
            cout << "Done burning fuel. Total millijoules burnt: " << quantity * energyYield << endl;
        }
};

burn()方法的逻辑对所有子类完全相同,唯一区别是子类的静态成员变量值不同。我的子类定义如下:

class Gasoline : public Fuel {
    protected:
        static const Pounds carbonDioxideEmmissionsPerLiter {5.07};
        static const Seconds burnTime {10};
        static const MilliJoulesPerLiter energyYield {34.8};
        Liters volume {0};
        MilliJoules totalMilliJoules {0};

    public:
        Gasoline() : Fuel {} {};
        Gasoline(int vol) : Fuel {vol} {};
};

class Coal : public Fuel {
    protected:
        static const Pounds carbonDioxideEmmissionsPerLiter {2.42};
        static const Seconds burnTime {5};
        static const MilliJoulesPerLiter energyYield {23.9};
        Liters volume {0};
        MilliJoules totalMilliJoules {0};
    public:
        Coal() : Fuel {} {};
        Coal(int vol) : Fuel {vol} {};
};

class Propane : public Fuel {
    protected:
        static const Pounds carbonDioxideEmmissionsPerLiter {3.17};
        static const Seconds burnTime {20};
        static const MilliJoulesPerLiter energyYield {25};
        Liters volume {0};
        MilliJoules totalMilliJoules {0};
    public:
        Propane() : Fuel {} {};
        Propane(int vol) : Fuel {vol} {};
};

调用子类对象的burn()方法时,总是使用基类的成员值。我不想把burn()复制到每个子类里,希望只在基类写一次,让它自动使用子类的属性值。尝试过以下方法均无效:

  • 将burn()设为virtual,未重写时仍使用基类版本;
  • 使用this->访问成员,无效果;
  • 给成员属性添加virtual关键字,语法错误;
  • 把静态常量改为非静态非常量,用初始化列表初始化子类属性,核心问题未解决;
  • 子类构造函数不调用基类构造函数,仍无作用。

解决方案

方法1:用纯虚函数封装静态常量访问

在基类中定义纯虚函数,让子类返回各自的静态常量值,基类burn()通过调用这些虚函数获取值,实现运行时动态绑定。

修改后的基类:

typedef float Pounds;
typedef int Seconds;
typedef float MilliJoulesPerLiter;
typedef float MilliJoules;
typedef int Liters;

class Fuel {
    protected:
        Liters volume {10};
        MilliJoules totalMilliJoules {0};
        // 定义纯虚函数,子类实现返回对应常量
        virtual Pounds getCO2EmissionsPerLiter() const = 0;
        virtual Seconds getBurnTime() const = 0;
        virtual MilliJoulesPerLiter getEnergyYield() const = 0;
        
    public:
        Fuel() : Fuel {10} {};
        
        Fuel(int vol) : volume(vol) {
            totalMilliJoules = volume * getEnergyYield();
        }

        virtual void burn() {
            cout << "Burning fuel..." << endl;
            int quantity = volume < getBurnTime() ? volume : getBurnTime();
            Sleep(quantity);
            volume -= quantity;
            totalMilliJoules /= volume * getEnergyYield();
            totalEmmissions += getCO2EmissionsPerLiter() * quantity;
            cout << "Done burning fuel. Total millijoules burnt: " << quantity * getEnergyYield() << endl;
        }
        
        virtual ~Fuel() = default; // 基类需添加虚析构函数
};

子类实现纯虚函数:

class Gasoline : public Fuel {
    protected:
        static const Pounds carbonDioxideEmmissionsPerLiter {5.07};
        static const Seconds burnTime {10};
        static const MilliJoulesPerLiter energyYield {34.8};

    public:
        Gasoline() : Fuel {} {};
        Gasoline(int vol) : Fuel {vol} {};
        
        Pounds getCO2EmissionsPerLiter() const override {
            return carbonDioxideEmmissionsPerLiter;
        }
        
        Seconds getBurnTime() const override {
            return burnTime;
        }
        
        MilliJoulesPerLiter getEnergyYield() const override {
            return energyYield;
        }
};

Coal、Propane子类做相同修改即可,基类burn()会自动调用子类的虚函数获取对应常量值,无需重复编写burn()逻辑。

方法2:使用模板基类

若常量在编译时即可确定,可将常量作为模板参数传递给基类,避免运行时多态开销。

定义模板基类:

typedef float Pounds;
typedef int Seconds;
typedef float MilliJoulesPerLiter;
typedef float MilliJoules;
typedef int Liters;

template<Pounds CO2_PER_LITER, Seconds BURN_TIME, MilliJoulesPerLiter ENERGY_YIELD>
class Fuel {
    protected:
        Liters volume {10};
        MilliJoules totalMilliJoules {0};
        
    public:
        Fuel() : Fuel {10} {};
        
        Fuel(int vol) : volume(vol) {
            totalMilliJoules = volume * ENERGY_YIELD;
        }

        void burn() {
            cout << "Burning fuel..." << endl;
            int quantity = volume < BURN_TIME ? volume : BURN_TIME;
            Sleep(quantity);
            volume -= quantity;
            totalMilliJoules /= volume * ENERGY_YIELD;
            totalEmmissions += CO2_PER_LITER * quantity;
            cout << "Done burning fuel. Total millijoules burnt: " << quantity * ENERGY_YIELD << endl;
        }
};

子类通过继承模板基类并指定参数实现:

class Gasoline : public Fuel<5.07, 10, 34.8> {
    public:
        Gasoline() : Fuel {} {};
        Gasoline(int vol) : Fuel {vol} {};
};

class Coal : public Fuel<2.42, 5, 23.9> {
    public:
        Coal() : Fuel {} {};
        Coal(int vol) : Fuel {vol} {};
};

class Propane : public Fuel<3.17, 20, 25> {
    public:
        Propane() : Fuel {} {};
        Propane(int vol) : Fuel {vol} {};
};

这种方法在编译时确定常量值,无运行时多态开销,代码更简洁,适合常量固定的场景。

方法3:将常量作为基类成员,构造时传递

把基类的静态常量改为普通成员变量,子类构造时将对应常量值传递给基类。

修改后的基类:

typedef float Pounds;
typedef int Seconds;
typedef float MilliJoulesPerLiter;
typedef float MilliJoules;
typedef int Liters;

class Fuel {
    protected:
        const Pounds carbonDioxideEmmissionsPerLiter;
        const Seconds burnTime;
        const MilliJoulesPerLiter energyYield;
        Liters volume {10};
        MilliJoules totalMilliJoules {0};
        
    public:
        Fuel(Pounds co2, Seconds bt, MilliJoulesPerLiter ey) 
            : carbonDioxideEmmissionsPerLiter(co2), burnTime(bt), energyYield(ey), volume(10) {
            totalMilliJoules = volume * energyYield;
        }
        
        Fuel(Pounds co2, Seconds bt, MilliJoulesPerLiter ey, int vol) 
            : carbonDioxideEmmissionsPerLiter(co2), burnTime(bt), energyYield(ey), volume(vol) {
            totalMilliJoules = volume * energyYield;
        }

        virtual void burn() {
            cout << "Burning fuel..." << endl;
            int quantity = volume < burnTime ? volume : burnTime;
            Sleep(quantity);
            volume -= quantity;
            totalMilliJoules /= volume * energyYield;
            totalEmmissions += carbonDioxideEmmissionsPerLiter * quantity;
            cout << "Done burning fuel. Total millijoules burnt: " << quantity * energyYield << endl;
        }
        
        virtual ~Fuel() = default;
};

子类构造时传递常量值:

class Gasoline : public Fuel {
    private:
        static const Pounds CO2_PER_LITER;
        static const Seconds BURN_TIME;
        static const MilliJoulesPerLiter ENERGY_YIELD;
    public:
        Gasoline() : Fuel(CO2_PER_LITER, BURN_TIME, ENERGY_YIELD) {};
        Gasoline(int vol) : Fuel(CO2_PER_LITER, BURN_TIME, ENERGY_YIELD, vol) {};
};

const Pounds Gasoline::CO2_PER_LITER = 5.07;
const Seconds Gasoline::BURN_TIME = 10;
const MilliJoulesPerLiter Gasoline::ENERGY_YIELD = 34.8;

Coal、Propane子类做相同修改即可,基类burn()直接使用这些成员值,无需重复编写逻辑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 09:17:08