- 在抽象类构造函数中调用虚函数的情况:
- 当在抽象类的构造函数中调用虚函数时,该调用不会表现出多态性,而是调用的当前类的版本。这是因为在构造函数执行期间,对象还未完全构造完成,此时虚函数表指针可能还未正确初始化,所以无法实现动态绑定。
- 代码示例:
#include <iostream>
class AbstractClass {
public:
AbstractClass() {
std::cout << "AbstractClass constructor" << std::endl;
virtualFunction();
}
virtual void virtualFunction() {
std::cout << "AbstractClass virtualFunction" << std::endl;
}
};
class ConcreteClass : public AbstractClass {
public:
ConcreteClass() {
std::cout << "ConcreteClass constructor" << std::endl;
}
void virtualFunction() override {
std::cout << "ConcreteClass virtualFunction" << std::endl;
}
};
int main() {
ConcreteClass obj;
return 0;
}
- 在上述代码中,
AbstractClass
是抽象类(这里为了演示方便,实际抽象类通常至少有一个纯虚函数),ConcreteClass
继承自 AbstractClass
并覆盖了 virtualFunction
。当创建 ConcreteClass
对象时,先调用 AbstractClass
的构造函数,在这个构造函数中调用 virtualFunction
,输出的是 AbstractClass virtualFunction
,而不是 ConcreteClass virtualFunction
,这证明了没有体现多态性。
- 避免因调用虚函数带来问题的设计方法:
- 避免在构造函数中调用虚函数:将需要的初始化逻辑封装到一个单独的非虚函数中,并在构造函数完成后调用该函数。
- 代码示例:
#include <iostream>
class AbstractClass {
public:
AbstractClass() {
std::cout << "AbstractClass constructor" << std::endl;
initialize();
}
void initialize() {
nonVirtualFunction();
}
virtual void nonVirtualFunction() {
std::cout << "AbstractClass nonVirtualFunction" << std::endl;
}
};
class ConcreteClass : public AbstractClass {
public:
ConcreteClass() {
std::cout << "ConcreteClass constructor" << std::endl;
}
void nonVirtualFunction() override {
std::cout << "ConcreteClass nonVirtualFunction" << std::endl;
}
};
int main() {
ConcreteClass obj;
return 0;
}
- 在这个示例中,
AbstractClass
的构造函数调用 initialize
函数,initialize
函数再调用 nonVirtualFunction
。在 ConcreteClass
中覆盖了 nonVirtualFunction
,由于构造函数完成后调用 initialize
,此时对象已构造完成,虚函数表指针已正确初始化,所以 nonVirtualFunction
的调用体现了多态性,输出 ConcreteClass nonVirtualFunction
。
- 原理阐述:
- 构造函数的任务是初始化对象的成员变量,并为对象准备好运行时的状态。在构造函数执行期间,对象处于部分构造状态,虚函数表指针可能还未完全正确设置。因此,在构造函数中调用虚函数,C++ 标准规定调用的是当前类的版本,而不是派生类覆盖的版本,以避免未定义行为。通过将初始化逻辑移到构造函数之外的函数中,可以确保对象完全构造后再调用可能被覆盖的函数,从而实现多态性。