虚函数深入挖掘-构造函数与虚函数【1】

    构造函数不能为虚函数,因为虚函数依赖虚函数表,构造函数尚未执行时,指向虚函数表的指针还不存在,因此显然构造函数无法成为虚函数。另外子类名字与父类不同,类的构造函数无法继承,更不能通过指针或引用去调用,因此跟虚函数也沾不上边。但是构造函数能否调用虚函数?按理说,构造函数没执行完,虚函数表都没有呢还。

    先来一个最基本的代码:

    

#include <stdio.h>

class A
{
public:
	A(){}
	~A(){}
	virtual void print(){printf("Base\n");}
};

class B:public A
{
	public:
		B(){}
		~B(){}
		void print(){printf("B\b");}
};


class C:public A
{
	public:
		C(){}
		~C(){}
		void print(){printf("C\b");}
};

int main()
{
	A a;
	B b;
	C c;

	A *p=NULL;
	int choice;
	printf("Input choice=");
	scanf("%d", &choice);

	switch(choice)
	{
	case 1:
		p=&a;
		break;
	case 2:
		p=&b;
		break;
	case 3:
		p=&c;
		break;
	default:
		p=&a;
	}

	p->print();
	getchar();
	return 0;
}

    结果:

    


    在构造函数里调用虚函数:

class A
{
public:
	A()
	{
		print();
	}
	~A(){}
	virtual void print(){printf("Base\n");}
};

class B:public A
{
	public:
		B():A()
		{
			print();
		}
		~B(){}
		void print(){printf("B\b");}
}; 
 
 
class C:public A 
{ 
	public: 
		void print(){printf("C\b");} 
		C() :A()
		{ 
			print(); 
		} 
		~C(){} 
		
}; 


class A
{
public:
	A()
	{
		print();
	}
	~A(){}
	virtual void print(){printf("Base\n");}
};


class B:public A
{
	public:
		B():A()
		{
			print();
		}
		~B(){}
		void print(){printf("B\b");}
}; 
 
 
class C:public A 
{ 
	public: 
		void print(){printf("C\b");} 
		C() :A()
					print(); 
				~C(){} 
		
}; int main()
{
	A a;
	printf("\n");
	B b;
	printf("\n");
	C c;

	
	return 0;
}



但是有几次只输出了Base,Base,Base。


参考资料:

http://dev.yesky.com/441/2033941.shtml

这里给出的解释是说,构造函数需要调用基类的构造函数,然后再执行本身的构造部分,所以呢,当本身的那部分还没开始的时候,编译器只认为这是一个基类对象。于是,就会调用基类对应的函数了!面试的时候只想着按指针来找,结果杯具了。考官gg说,你回去跑跑看。幸好后来让我写对象模型写对了。



基类构造期间,虚函数从来不会向下匹配(go down)到派生类。取而代之的是,那个对象的行为就好像它的类型是基类。非正式地讲,基类构造期间,虚函数禁止。 这个表面上看起来匪夷所思的行为存在一个很好的理由。因为基类的构造函数在派生类构造函数之前执行,当基类构造函数运行时,派生类数据成员还没有被初始化。如果基类构造期间调用的虚函数向下匹配(go down)到派生类,派生类的函数理所当然会涉及到本地数据成员,但是那些数据成员还没有被初始化。这就会为未定义行为和悔之晚矣的调试噩梦开了一张通行证。调用涉及到一个对象还没有被初始化的部分自然是危险的,所以 C++ 告诉你此路不通。

  在实际上还有比这更多的更深层次的原理。在派生类对象的基类构造期间,对象的类型是那个基类的。不仅虚函数会解析到基类,而且语言中用到运行时类型信息(runtime type information)的配件(例如,dynamic_cast和 typeid),也会将对象视为基类类型。在我们的例子中,当 Transaction 构造函数运行初始化 BuyTransaction 对象的基类部分时,对象的类型是 Transaction。C++ 的每一个配件将以如下眼光来看待它,并对它产生这样的感觉:对象的 BuyTransaction 特有的部分还没有被初始化,所以安全的对待它们的方法就是视若无睹。在派生类构造函数运行之前,一个对象不会成为一个派生类对象。

  同样的原因也适用于析构过程。一旦派生类析构函数运行,这个对象的派生类数据成员就被视为未定义的值,所以 C++ 就将它们视为不再存在。在进入基类析构函数时,对象就成为一个基类对象,C++ 的所有配件——虚函数,dynamic_casts 等——都如此看待它。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值