一道C++笔试题说一些知识
在C/C++程序员的求职路上,在笔试中经常会遇到的题目就是继承方面还有构造函数方面的知识,下面我从一个例子给大家讲解一下这其中的知识点。
先看下面这个代码:
#include <iostream>
using namespace std;
class B
{
public:
B()
{
init();
}
B(int i)
{}
virtual void init()
{
cout<<"Base::init()"<<endl;
}
virtual void Func()
{
cout<<"Base::Func()"<<endl;
virtual_Test();
non_virtual_Test();
}
void Func1()
{
cout<<"Base::Func1()"<<endl;
virtual_Test();
non_virtual_Test();
}
virtual void virtual_Test()
{
cout<<"Base::virtual_Test()"<<endl;
}
void non_virtual_Test()
{
cout<<"Base::non_virtual_Test()"<<endl;
}
virtual void Delete()
{
cout<<"Base::Delete()"<<endl;
}
~B()
{
Delete();
}
};
class D:public B
{
public:
D():B()
{
init();
}
void init()
{
cout<<"Derived::init()"<<endl;
}
void Func()
{
cout<<"Derived::Func()"<<endl;
virtual_Test();
non_virtual_Test();
}
void Func1()
{
cout<<"Derived::Func1()"<<endl;
virtual_Test();
non_virtual_Test();
}
void virtual_Test()
{
cout<<"Derived::virtual_Test()"<<endl;
}
void non_virtual_Test()
{
cout<<"Derived::non_virtual_Test()"<<endl;
}
void Delete()
{
cout<<"Derived::Delete()"<<endl;
}
~D()
{
Delete();
}
};
int main()
{
B *b = new D();
b->Func();
b->Func1();
delete b;
getchar();
return 0;
}
运行结果如下:
Base::init()
Derived::init()
Derived::Func()
Derived::virtual_Test()
Derived::non_virtual_Test()
Base::Func1()
Derived::virtual_Test()
Base::non_virtual_Test()
Base::Delete()
下面开始一一解释:
1、B *b = new D();
此句的执行结果为:
Base::init()
Derived::init()
这个很容易明白,构造一个对象的时候,最先执行的就是最“上”的那个基类。也就是如果你的类是派生而来,首先执行的构造函数就是基类的构造函数,基类如果也是派生类,一样也要先执行派生类的构造函数,所以这里先执行Base再执行Derived。
2、b->Func();
此句的执行结果为:
Derived::Func()
Derived::virtual_Test()
Derived::non_virtual_Test()
首先,先让大家知道什么是静态绑定,什么是动态绑定。很简单,举个例子说明。
D *pD = new D(); 静态类型是*D,动态类型是*D
B *pB = pD; 静态类型是*B,即定义的类型;动态类型为*D,即所指向的类型
所谓静态绑定就是在编译期就确定的类型,如上所说的静态类型;动态绑定就是在运行期才确定的类型,如上所说的动态类型。
看b->Func();因为Func()是virtual函数,所以对象调用的是动态类型,也就Derived::Func(),Func()函数里面又调用了两个函数virtual_Test()和non_virtual_Test(),一样遵循上面的规制,virtual_Test()调用的是动态类型的Derived::virtual_Test(),而non_virtual_Test()调用的是Derived::Func()所属对象的Derived::non_virtual_Test()。
3、b->Func1();
此句的执行结果为:
Base::Func1()
Derived::virtual_Test()
Base::non_virtual_Test()
对于Func1()的执行也类似,Func1()是non-virtual函数,所以调用对象调用的是静态类型的Base::Func1(),然后virtual_Test()调用的是动态类型的Derived:: virtual_Test(),而non_virtual_Test()调用的是Base::Func1()所属对象的Base::non_virtual_Test()。
不近视的程序YUAN可能会发现Base构造函数中调用了virtual函数,按上面的规则,调用的应该是Derived::init()才对,是事实却是调用了Base::init()。这里要说明的就是这是特殊的情况,在构造函数中是没有多态的,这也符合常理,因为构造函数就是确定一个对象的内容的时候,那么对象都还确定,所以也就实现不了多态。
4、delete b;
此句的执行结果为:
Base::Delete()
按照对象销毁的顺序,是跟构造的顺序相反的,也就是先销毁派生类的再销毁基类的,但这里没有调用~D()这个函数,等一下再说明原因。调用了~B()函数,~B()函数中调用了Delete()函数,它是一个virtual函数,遵循上面说到的规则,是执行~B()所属对象的Base::Delete()。为什么会没有执行到Derived的析构函数呢,我们看下面这则规则:C++明白指出,当derived class 对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义——实际执行时通常发生的是对象的derived成分没有被销毁。因为析构函数不是virtual的,所以并没有执行derived的析构函数,由此引申出:如果基类有virtual成员函数,就应该声明一个virtual的析构函数。这是好的编程习惯,有利于减少内存泄露。还有就是不要改变继承而来的non-virtual函数,这是不好的编程风格。