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

C++中向上转型变量为何调用派生类虚函数?附相关问题

C++多态与类型转换问题解答

提供的代码

类定义

class Person
{
public:
    Person(const std::string& name, std::size_t age);

    virtual void print() const;
    void inc_age() { ++_age; }

private:
    std::string _name;
    std::size_t _age;
};

class Employee : public Person
{
public:
    Employee(const std::string& name, std::size_t age, double salary);

    virtual void print() const override;
    void add_bonus(double percent);

private:
    double _salary;
};

main函数

int main()
{
    Person mike{"Mike"s, 20};
    Employee manager{"Sam"s, 35, 100};

    Person* p{&manager};
    if (p) p->print(); // this line
    std::cout << '\n';

    return 0;
}

问题

  1. 原本认为向上转型后应调用Person::print(),但实际却调用了Employee::print(),即使使用static_cast<Person*>(p)->print()也是如此。想了解:如何在不发生切片(切片是否属于未定义行为?)的情况下调用Person::print()?
  2. 教授提到:若类不是多态类(无虚函数),使用dynamic_cast不会报错,会静默产生未定义行为。该说法是否正确?有什么方法可以避免这种情况?

解答

问题1:调用基类print()且避免切片

  • 显式类名限定调用:直接通过基类名限定函数调用,就能绕过虚函数的动态绑定机制,强制调用基类版本的print()。写法如下:
    p->Person::print();
    
    这种操作不会触发切片——切片是指将派生类对象赋值给基类对象时,派生类独有成员被截断的行为,这里仅调用基类成员函数,没有对象赋值操作,完全不存在切片问题。
  • 切片的性质:切片本身是标准定义的合法行为,不属于未定义行为。当把Employee对象赋值给Person对象时,只会保留基类部分的成员,派生类的_salary会被丢弃,这是C++标准明确规定的行为。

问题2:非多态类使用dynamic_cast的行为

  • 教授的说法是正确的。根据C++标准,dynamic_cast仅允许用于多态类型(即包含至少一个虚函数的类)。若对非多态类使用dynamic_cast,属于未定义行为——程序可能崩溃、输出异常结果,或看似正常运行但行为不可预测,且编译器不强制报错(部分编译器会给出警告,但并非必须)。
  • 避免方法:
    • 限定dynamic_cast的使用场景:仅对多态类使用该转换,通常建议将基类的析构函数设为虚函数,这既是多态类的必要条件,也是避免内存泄漏的良好编程习惯。
    • 替代方案:若类无需多态但必须转换,优先使用static_cast(需确保你明确知晓指针/引用的实际类型,保证转换安全);或重新设计类结构,将其改为多态类以支持dynamic_cast的安全使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 10:32:39