C++中如何在以基类为参数的函数中获取派生类默认值
问题根源:对象切片与成员设计问题
你的代码存在两个核心问题:
- 对象切片:Source类直接存储
Particle对象,当你把Proton/Electron对象传入setParticle时,会发生对象切片——派生类的特有数据被丢弃,仅保留基类Particle的部分,导致虚函数调用时只能访问基类的空成员。 - 派生类成员重定义:
Electron和Proton类重新定义了name、mass等私有成员,而非初始化基类的protected成员,不符合继承的设计逻辑,也容易造成混淆。
解决方案:用指针/智能指针实现多态
要保留派生类的多态特性,Source类不能直接存储基类对象,需存储基类指针或智能指针。以下是两种可行的修改方案:
方案1:使用裸指针(需手动管理内存)
步骤1:修正基类与派生类的成员设计
让派生类初始化基类的protected成员,而非重新定义私有成员:
#include <iostream> #include <string> class Particle{ protected: std::string name; double mass; double charge; double energy; double sigma; public: virtual ~Particle() = default; // 必须添加虚析构函数,确保派生类对象正确销毁 virtual std::string getName() {return name;}; virtual double getCharge() {return charge;}; virtual double getMass() {return mass;}; }; class Electron : public Particle{ public: Electron() { name = "e-"; mass = 9.1093837e-31; charge = -1.60217663e-19; } std::string getName() override {return name;}; double getMass() override {return mass;}; double getCharge() override {return charge;}; }; class Proton : public Particle{ public: Proton() { name = "p+"; mass = 1.67262192e-27; charge = +1.60217663e-19; } std::string getName() override {return name;}; double getMass() override {return mass;}; double getCharge() override {return charge;}; };
步骤2:修改Source类,用指针存储Particle
class Source{ private: Particle* particle; // 存储基类指针 int noPrimaries; public: Source() : particle(nullptr) {} ~Source() { delete particle; } // 销毁指针指向的对象 Particle* getParticle() {return particle;}; int getNoPrimaries() {return noPrimaries;}; void setParticle(Particle* p) { delete particle; // 先释放旧对象 particle = p; }; void setNoPrimaries(int n) {noPrimaries=n;}; };
步骤3:修改main函数与getSourceData函数
void getSourceData(Source& source){ // 使用引用避免拷贝 Particle* p = source.getParticle(); if(p){ printf("Particle name = %s\n", p->getName().c_str()); printf("Particle mass = %e\n", p->getMass()); printf("Particle charge = %e\n", p->getCharge()); } printf("Number of primaries = %i\n", source.getNoPrimaries()); } int main(){ Source source; source.setParticle(new Proton()); // 传入派生类对象的指针 source.setNoPrimaries(12); getSourceData(source); // 切换为Electron测试 source.setParticle(new Electron()); getSourceData(source); return 0; }
方案2:使用智能指针(推荐,自动管理内存)
用std::unique_ptr可以避免手动管理内存,更安全:
修改Source类:
#include <memory> // 引入智能指针头文件 class Source{ private: std::unique_ptr<Particle> particle; int noPrimaries; public: Particle* getParticle() {return particle.get();}; int getNoPrimaries() {return noPrimaries;}; void setParticle(std::unique_ptr<Particle> p) { particle = std::move(p); }; void setNoPrimaries(int n) {noPrimaries=n;}; };
修改main函数:
int main(){ Source source; source.setParticle(std::make_unique<Proton>()); source.setNoPrimaries(12); getSourceData(source); source.setParticle(std::make_unique<Electron>()); getSourceData(source); return 0; }
关键知识点说明
- 虚析构函数:基类必须定义虚析构函数,否则通过基类指针销毁派生类对象时会导致未定义行为。
- 对象切片:用派生类对象给基类对象赋值时,派生类的额外数据会被“切掉”,仅保留基类部分,多态机制失效。
- 智能指针:
std::unique_ptr是独占式智能指针,自动管理对象生命周期,避免内存泄漏,是C++管理动态内存的首选方式。
内容的提问来源于stack exchange,提问作者lghizoni
相关产品推荐
相关产品推荐

