面试题答案
一键面试内存布局角度
- 常对象内存布局:
- 当定义一个
Derived
类的常对象时,该对象在内存中的布局遵循继承体系的规则。Derived
类对象的内存中,首先是Base
类部分,然后是Derived
类自己新增的成员。 - 由于对象是常对象,整个对象在内存中的数据部分是只读的(不考虑
mutable
成员)。
- 当定义一个
- 调用成员函数限制:
- 只能调用从
Base
类继承过来的常成员函数。因为普通成员函数有可能修改对象的数据成员,而常对象的数据成员是只读的,调用普通成员函数会违反对象的常量性。例如:
- 只能调用从
class Base {
public:
void nonConstFunc() { /* 可修改数据成员 */ }
void constFunc() const { /* 不能修改数据成员 */ }
};
class Derived : public Base {};
int main() {
const Derived d;
// d.nonConstFunc(); // 错误,常对象不能调用普通成员函数
d.constFunc(); // 正确,常对象可以调用常成员函数
return 0;
}
函数重写角度
- 重写规则与常对象调用:
- 如果
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
类的常对象,从内存布局看,其数据只读限制了只能调用常成员函数;从函数重写角度,重写的虚函数也必须是常成员函数才能被常对象正确调用。