如何仅定义一次基类方法,使子类调用时使用子类成员而非基类?
问题描述
我定义了如下基类:
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
相关产品推荐
相关产品推荐

