面试题答案
一键面试- 构造函数非虚声明对对象创建过程的影响
- 内存分配:
- 当创建一个对象时,首先会为该对象分配内存。对于包含继承关系的类层次结构,内存分配遵循一定规则。系统会根据对象的类型(包括基类和派生类部分),在堆(如果使用
new
动态分配)或栈(如果是局部对象)上分配足够的空间。例如,如果有一个基类Base
和一个派生类Derived
继承自Base
,创建Derived
对象时,会为Base
部分和Derived
新增的成员变量分配连续的内存空间。
- 当创建一个对象时,首先会为该对象分配内存。对于包含继承关系的类层次结构,内存分配遵循一定规则。系统会根据对象的类型(包括基类和派生类部分),在堆(如果使用
- 基类和派生类构造函数的调用顺序:
- 先调用基类的构造函数,然后再调用派生类的构造函数。这是因为在派生类对象构造时,其基类部分必须先被初始化。例如:
- 内存分配:
class Base {
public:
Base() {
std::cout << "Base constructor" << std::endl;
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived constructor" << std::endl;
}
};
int main() {
Derived d;
return 0;
}
在上述代码中,创建Derived
对象d
时,会先输出Base constructor
,再输出Derived constructor
。
- 将构造函数声明为虚函数带来的潜在问题
- 语义问题:虚函数机制是基于对象的动态类型来决定调用哪个函数版本,而在对象构造期间,对象还未完全形成,其动态类型是不完整的。构造函数的主要目的是初始化对象的成员变量,将其声明为虚函数与构造函数的基本职责相冲突。
- 性能问题:虚函数调用需要通过虚函数表(vtable)来实现,这会增加额外的开销。对于构造函数这种在对象创建时频繁调用的函数,额外的虚函数表查找开销会降低程序的性能。
- 实现问题:由于构造函数调用顺序的原因,在基类构造时,派生类部分还未初始化。如果基类构造函数是虚函数,并且在基类构造函数中调用了虚函数,那么实际调用的将是基类版本的函数,而不是派生类版本(即使最终创建的是派生类对象),这往往不符合预期的多态行为。例如:
class Base {
public:
virtual void print() {
std::cout << "Base print" << std::endl;
}
Base() {
print();
}
};
class Derived : public Base {
public:
void print() override {
std::cout << "Derived print" << std::endl;
}
Derived() {}
};
int main() {
Derived d;
return 0;
}
在上述代码中,Base
构造函数中调用print
函数,输出的是Base print
,而不是Derived print
,这与期望的多态行为不符。