代码改变世界

程序员面试宝典三

2012-08-22 10:27  javaspring  阅读(207)  评论(0编辑  收藏  举报

1、基类的析构函数声明为virtual的好处?

    假设基类CBase,其派生类CChild,有如下代码:

    CBase *pBase=new CChild;   //new CChild构造对象时,先调用基类CBase的构造函数,然后调用CChild的构造函数,析构时顺序应该恰好相反。

    delete pBase;

    如果CBase的析构函数定义为virtual,那么pBase指针被撤销时,就会先调用CChild的析构函数,然后调用CBase的析构函数。

    而如果CBase的析构函数不是virtual,那么只会调用CBase'的析构函数,从而导致内存泄漏。

2、构造函数为什么不可以声明为virtual型?

    虚函数采用虚调用的办法。虚调用是一种可以在只有部分信息的情况下工作的机制,特别允许我们调用一个只知道接口而不知道其准确对象类型的函数。但是如果创造一个对象,你势必要知道对象的准确类型,因此构造函数不能为virtual型。

3、是否可以把类的每个函数(构造函数除外)声明为virtual型?

    不行,因为虚函数是有代价的:由于每个虚函数的对象都必须维护一个v表,因此在使用虚函数的时候会产生一个系统开销。如果仅仅是很小的类,且不想派生其他的类,根本没有必要使用虚函数。

4、虚函数和虚继承

    虚函数:类将维护一个虚函数表,来记录对应的函数入口地址

    虚继承:派生类将维护一个虚类指针,来指向其父类。

5、虚函数的入口地址和普通函数区别?

    每个虚函数在vtable中占了一个表项,该表项保存着虚函数的入口地址。当创建一个包含虚函数的对象时,该对象在头部附加一个指针,指向vtable中相应的位置。调用虚函数的时候,不管你是用什么指针调用的,它先跟据vtable找到入口地址再执行,从而实现了动态联编。而不像普通函数那样简单的跳转到一个固定的地址。

6、基类和派生类地址和布局,下面代码输出

#include "stdafx.h"
#include <iostream.h>
#include <string.h>

class A
{
	int a;
};

class B
{
	int b;
};

class C:public A,public B //多重继承
{
	int c;
};

void main()
{
	C *c=new C;
	B *b=dynamic_cast<B*>(c);
	A *a=dynamic_cast<A*>(c);

	if (c==b)  //两端数据类型不同,进行隐式转换,变成 c=(C*)b ; equal
		cout<<"equal"<<endl;            
	else
		cout<<"not equal"<<endl;

	if (int(c)==int((C*)b)) //比较c和b隐士转换为(C*); equal
		cout<<"equal"<<endl;
	else
		cout<<"not equal"<<endl;

	if (int(c)==int(b))    //c和b的值不同,转换为int时也不相同; not equal
		cout<<"equal"<<endl;
	else
		cout<<"not equal"<<endl;
}