MST

星途 面试题库

面试题:C++ 常对象与继承中的特性

假设有一个基类Base和一个派生类Derived,Base类中有一些常成员函数和普通成员函数。当定义一个Derived类的常对象时,在调用从Base类继承过来的成员函数方面,有哪些需要注意的地方?请从内存布局和函数重写的角度进行分析。
27.5万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

内存布局角度

  1. 常对象内存布局
    • 当定义一个Derived类的常对象时,该对象在内存中的布局遵循继承体系的规则。Derived类对象的内存中,首先是Base类部分,然后是Derived类自己新增的成员。
    • 由于对象是常对象,整个对象在内存中的数据部分是只读的(不考虑mutable成员)。
  2. 调用成员函数限制
    • 只能调用从Base类继承过来的常成员函数。因为普通成员函数有可能修改对象的数据成员,而常对象的数据成员是只读的,调用普通成员函数会违反对象的常量性。例如:
class Base {
public:
    void nonConstFunc() { /* 可修改数据成员 */ }
    void constFunc() const { /* 不能修改数据成员 */ }
};
class Derived : public Base {};
int main() {
    const Derived d;
    // d.nonConstFunc(); // 错误,常对象不能调用普通成员函数
    d.constFunc(); // 正确,常对象可以调用常成员函数
    return 0;
}

函数重写角度

  1. 重写规则与常对象调用
    • 如果Derived类重写了Base类的虚函数,在常对象调用时,会根据动态绑定的规则调用Derived类中重写的版本。但这个重写版本必须是常成员函数。例如:
class Base {
public:
    virtual void func() const { std::cout << "Base::func" << std::endl; }
};
class Derived : public Base {
public:
    void func() const override { std::cout << "Derived::func" << std::endl; }
};
int main() {
    const Derived d;
    Base* ptr = &d;
    ptr->func(); // 输出 "Derived::func",动态绑定到Derived类的重写版本
    return 0;
}
  • 如果Derived类重写的虚函数不是常成员函数,而Base类中的虚函数是常成员函数,这会导致编译错误,因为重写函数的常量性必须与基类虚函数一致(协变返回类型等特殊情况除外)。例如:
class Base {
public:
    virtual void func() const { std::cout << "Base::func" << std::endl; }
};
class Derived : public Base {
public:
    void func() { std::cout << "Derived::func" << std::endl; } // 错误,重写函数不是常成员函数
};

总结:对于Derived类的常对象,从内存布局看,其数据只读限制了只能调用常成员函数;从函数重写角度,重写的虚函数也必须是常成员函数才能被常对象正确调用。