题目:以下程序运行的结果是什么?
class A {
public:
A() {
std::cout << "create A" << std::endl;
virFunc();
}
virtual void virFunc() {
std::cout << "A virFunc" << std::endl;
}
};
class B : public A {
public:
B() {
std::cout << "create B" << std::endl;
virFunc();
}
virtual void virFunc() {
std::cout << "B virFunc" << std::endl;
}
};
int main() {
A *p = new B();
delete p;
return 0;
}
这可能有个陷阱,创建子类对象,构造函数先调用父类的,再调用子类的构造函数,这里又由于 virFunc
是虚函数,所以根据虚函数的机制,应该会有以下输出:
create A
B virFunc
creat B
B virFunc
貌似看起来没啥问题,可是实际上上述输出的第二行是错误的,不信你可以在自己的电脑上试试,正确的答案应该为:
create A
A virFunc
create B
B virFunc
很多同学不理解,virFunc
不是虚函数吗?怎么会调用父类本身的那个实现体,不是应该调用子类的那个吗?
请你再回想下虚函数机制的作用时机…
虚函数产生多态的时机是:
- 1) 通过父类的指针去调用虚函数,此时父类的指针实际上指向某个子类型。
- 2) 通过父类的引用去调用虚函数,此时父类的引用实际上引用的是某个子类型。
这样就可以理解为什么上述第二行的输出结果会是 A virFunc
了,当 new B()
执行时,首先调用父类的构造函数,即调用 A
的构造函数,我们都知道,在类的成员函数中有一个隐藏的 this
指针,代表的是当前对象的地址,在调用 A()
时,传递给 A()
的 this
指针应该是新创建的 B
的对象的地址,所以 A()
可类似为:
A(this) { // 此时的 this 为新创建的 B 的对象的指针
std::cout << "create A" << endl;
this->A::virFunc(); // 这里实际上是通过子类的指针去调用父类中的函数,没有产生多态的效应
}
所以对于 C++ 的很多语法,只有真正理解了,或者说了解其底层相关的机制,才能在遇到相关问题的时候能够胸有成竹,而不会出现模棱两可的现象。
另外插一个额外的语法点,也是面试中的题目:static member functions
不能被 const
修饰,会出现编译错误。但是 const
可以修饰 static data members
。