下列关于虚函数的说法正确的是()
A、在构造函数中调用类自己的虚函数,虚函数的动态绑定机制还会生效。
B、在析构函数中调用类自己的虚函数,虚函数的动态绑定机制还会生效。
C、静态函数不可以是虚函数
D、虚函数可以声明为inline
此题答案给的是BCD,对B很怀疑,因为:
1. 先构造基类然后构造派生类
2. 先析构派生类在析构基类
3. 构造基类的时候派生类还没有,所以基类中的构造函数调用的虚函数是基类中定义的虚函数
4. 析构基类的时候派生类已经析构,所以基类中析构函数调用的虚函数是基类中的虚函数
c++中类的动态绑定机制从构造函数完成之后开始生效,到析构函数调用之前终止。
通过下面代码验证:
1、首先对于AB选项,我做了如下代码:
1 #include <iostream> 2 using namespace std; 3 class ClassA 4 { 5 public: 6 ClassA() 7 { 8 cout<<"classA-begin"<<endl; 9 OutPut(); 10 cout<<"classA-endl"<<endl; 11 } 12 virtual ~ClassA() 13 { 14 cout<<"~classA-begin"<<endl; 15 OutPut(); 16 cout<<"~classA-endl"<<endl; 17 } 18 void virtual OutPut() 19 { 20 cout<<"A"<<endl; 21 } 22 }; 23 24 class ClassB:public ClassA 25 { 26 public: 27 ClassB() 28 { 29 cout<<"ClassB-begin"<<endl; 30 OutPut(); 31 cout<<"ClassB-endl"<<endl; 32 } 33 virtual ~ClassB() 34 { 35 cout<<"~ClassB-begin"<<endl; 36 OutPut(); 37 cout<<"~ClassB-endl"<<endl; 38 } 39 void OutPut() 40 { 41 cout<<"B"<<endl; 42 } 43 44 }; 45 46 47 int main() 48 { 49 ClassA *a=new ClassB(); 50 a->OutPut(); 51 delete a; 52 getchar(); 53 return 0; 54 55 }
在vs2010中输出结果为:
可以看到,在new ClassB时,虽然在父类ClassA的构造函数调了的是被ClassB覆盖的虚函数Output(),但是实际上还是调用的ClassA的OutPut。这是因为
继承类在构造的时候总是首先调用其基类的构造函数来对属于其基类的部分进行构造,在这个时候,整个类被当作基类来处理,继承类的部分对整个类来说好像不存在一样,直到基类的构造函数退出并进入继承类的构造函数,该类才被当作继承类来出来处理。对析构也一样,只是析构的顺序正好相反。
由此可知AB错误。(可以参考:http://www.cnblogs.com/xiaoxibo/archive/2011/07/18/2212612.html)
对于C选项:
因为静态成员函数没有this,也就没有存放vptr的地方,同时其函数的指针存放也不同于一般的成员函数,其无法成为一个对象的虚函数的指针以实现由此带来的动态机制。静态是编译时期就必须确定的,虚函数是运行时期确定的。故C项正确。
对于D选项:
inline函数和virtual函数有着本质的区别,inline函数是在程序被编译时就展开,在函数调用处用整个函数体去替换,而virtual函数是在运行期才能够确定如何去调用的,因而inline函数体现的是一种编译期机制,virtual函数体现的是一种运行期机制。
因此,内联函数是个静态行为,而虚函数是个动态行为,他们之间是有矛盾的。
函数的inline属性是在编译时确定的, 然而,virtual的性质则是在运行时确定的,这两个不能同时存在,只能有一个选择,文件中声明inline关键字只是对编译器的建议,编译器是否采纳是编译器的事情。
我并不否认虚函数也同样可以用inline来修饰,但你必须使用对象来调用,因为对象是没有所谓多态的,多态只面向行为或者方法,但是C++编译器,无法保证一个内联的虚函数只会被对象调用,所以一般来说,编译器将会忽略掉所有的虚函数的内联属性。
所以D正确。
故此题答案为CD