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

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;
}

关键知识点说明
  1. 虚析构函数:基类必须定义虚析构函数,否则通过基类指针销毁派生类对象时会导致未定义行为。
  2. 对象切片:用派生类对象给基类对象赋值时,派生类的额外数据会被“切掉”,仅保留基类部分,多态机制失效。
  3. 智能指针:std::unique_ptr是独占式智能指针,自动管理对象生命周期,避免内存泄漏,是C++管理动态内存的首选方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 06:06:22