面试题答案
一键面试运行时多态方面
- 问题:当派生类常对象调用虚函数时,如果基类虚函数没有被声明为
const
,会导致编译错误。因为常对象只能调用常成员函数,而非常成员函数无法修改常对象的状态。 - 解决方法:将基类中的虚函数声明为
const
,这样派生类重写的虚函数也会自动成为const
成员函数,常对象就可以调用。
class Base {
public:
virtual void func() const {
// 基类虚函数,被声明为 const
std::cout << "Base::func()" << std::endl;
}
};
class Derived : public Base {
public:
void func() const override {
// 派生类重写的虚函数,也为 const
std::cout << "Derived::func()" << std::endl;
}
};
int main() {
const Derived d;
Base* ptr = &d;
ptr->func(); // 运行时多态,调用 Derived::func()
return 0;
}
底层原理:C++通过虚函数表(vtable)实现运行时多态。当对象为常对象时,编译器会检查调用的成员函数是否为 const
,如果不是则报错。虚函数表中存储的是虚函数的地址,通过对象的虚指针(vptr)找到对应的虚函数表,从而实现运行时多态。
访问控制方面
- 问题:如果基类成员变量或函数的访问修饰符为
private
,派生类常对象无法直接访问。即使派生类重写了成员函数,如果重写函数访问了基类private
成员,常对象调用派生类函数也可能出现问题。 - 解决方法:将基类需要被派生类访问的成员设为
protected
或提供public
的访问接口(如get
和set
函数)。
class Base {
protected:
int data;
public:
Base(int value) : data(value) {}
int getData() const {
return data;
}
};
class Derived : public Base {
public:
Derived(int value) : Base(value) {}
void printData() const {
// 访问基类 protected 成员
std::cout << "Data: " << getData() << std::endl;
}
};
int main() {
const Derived d(10);
d.printData();
return 0;
}
底层原理:编译器在编译阶段根据访问修饰符检查对象对成员的访问权限。private
成员只能在类内部访问,protected
成员可以在派生类中访问,public
成员可以在任何地方访问。
内存管理方面
- 问题:如果基类没有虚析构函数,当通过基类指针删除派生类常对象时,可能导致内存泄漏。因为不会调用派生类的析构函数,派生类中分配的资源无法释放。
- 解决方法:将基类析构函数声明为虚析构函数。
class Base {
public:
virtual ~Base() {
std::cout << "Base::~Base()" << std::endl;
}
};
class Derived : public Base {
private:
int* arr;
public:
Derived() {
arr = new int[10];
}
~Derived() override {
delete[] arr;
std::cout << "Derived::~Derived()" << std::endl;
}
};
int main() {
Base* ptr = new Derived();
delete ptr;
return 0;
}
底层原理:当基类析构函数为虚时,通过基类指针删除派生类对象,会根据对象实际类型调用相应的析构函数。如果基类析构函数不是虚的,delete 操作只会调用基类析构函数,导致派生类资源无法释放。