面试题答案
一键面试1. 构造函数调用顺序
当创建 B
类的对象时,无论 B
类使用哪个重载的构造函数,总是先调用 A
类的构造函数,然后再调用 B
类自身的构造函数。这是因为在创建子类对象时,需要先初始化父类部分,然后再初始化子类特有的部分。
2. 成员初始化列表情况
- 父类构造函数选择:如果
B
类的构造函数在成员初始化列表中没有显式调用A
类的特定构造函数,那么编译器会尝试调用A
类的默认构造函数(如果存在)。如果A
类没有默认构造函数,编译器会报错。 - 显式调用父类构造函数:如果
B
类的构造函数在成员初始化列表中显式调用A
类的某个重载构造函数,那么就会调用该指定的A
类构造函数。例如:
class A {
public:
A() {
std::cout << "A default constructor" << std::endl;
}
A(int num) {
std::cout << "A constructor with int: " << num << std::endl;
}
};
class B : public A {
public:
B() : A() {
std::cout << "B default constructor" << std::endl;
}
B(int num) : A(num) {
std::cout << "B constructor with int" << std::endl;
}
};
int main() {
B b1;
B b2(10);
return 0;
}
在上述代码中:
-
B b1;
会先调用A
类的默认构造函数,再调用B
类的默认构造函数。 -
B b2(10);
会先调用A
类带int
参数的构造函数,再调用B
类带int
参数的构造函数。 -
成员初始化顺序:在
B
类构造函数的成员初始化列表中,除了调用父类构造函数外,还会按照成员变量在类中声明的顺序进行初始化,而不是按照成员初始化列表中出现的顺序。例如:
class B : public A {
private:
int a;
int b;
public:
B(int num) : b(num), a(b + 1) {
std::cout << "B constructor with int" << std::endl;
}
};
这里虽然在成员初始化列表中先写了 b(num)
,后写了 a(b + 1)
,但实际上会先初始化 a
(因为 a
先声明),此时 b
还未初始化,所以 a
的初始化值是未定义的。为避免这种情况,应按照声明顺序初始化成员变量。