c++类构造函数、析构函数与虚函数之间的那点小事

Q:类的构造函数是否可以是虚函数?

A:不可以。

      首先,设置后编译出错,语法上c++并不允许把类的构造函数声明为虚函数;

      其次,抛开语法层面,类对象构造的时候是按照继承层次从上至下开始构造。创建一个类对象的时候是先分配整个类需要的存储空间,然后开始执行构造函数,举个简单的例子:


class A
{
public:
	A() {}
	virtual void Print()
	{
		printf("this is A.\n");
	}
};

class B : public A
{
public:
	B() {}
	virtual void Print()
	{
		printf("this is B.\n");
	}
};

int main(int argc, char** argv)
{
	B b;
	return 0;
}

       对象b构造的时候会先执行父类A的构造函数,父类A构造函数执行的时候并不知道继承关系是如何的,换言之父类A并不知道类B的存在,此时若A的构造函数为虚函数,也只会调用A自身的构造函数,虚函数的多态性并不会体现。

     最后,我们知道类的多态性是通过虚表指针来实现的,虚表指针在构造函数调用的时候初始化,而我们把构造函数设置为虚函数,也就是说这个时候要实现多态性必须要有已经初始化的虚表指针,然而虚表指针这时候并未初始化,这就形成了一个悖论。另外有一点就是初始化父类部分虚表指针指向的是父类自己的虚表,父类自己的虚表只会存放父类自己的虚函数的指针,表里并没有子类虚函数的指针,所以也不可能形成多态性。



Q:构造函数是否可以调用虚函数?

A:可以。但是形成不了多态性。

举个例子:

#include <stdio.h>
#include <typeinfo>
using namespace std;
class A;
typedef void(*Fun)(A*);

class A
{
private:
	int m_a;
public:
	A(int a) : m_a(a) 
	{
		//printf("%s\n", typeid(*this).name());
		//printf("this: %p\n", this);
		printf("vptr: %p\n", *(int*)this);
		printf("%p\n", (int*)*((int*)(*(int*)this) + 1));
		printf("%p\n", (int*)*((int*)(*(int*)this) + 2));
		((Fun)*((int*)(*(int*)this) + 1))(this);
		((Fun)*((int*)(*(int*)this) + 2))(this);
		printf("\n");
	}
	
        virtual ~A()
	{
	};
	
        virtual void show() const
	{
		printf("show a: %d\n", m_a);
	}
	
        virtual void disp() const
	{
		printf("disp a: %d\n", m_a);
	}
};

class B : public A
{
private:
	int m_b;
public:
	B(int a, int b) : m_b(b), A(a)
	{
		//printf("this: %p\n", this);
		printf("vptr: %p\n", *(int*)this);
		printf("%p\n", (int*)*((int*)(*(int*)this) + 1));
		printf("%p\n", (int*)*((int*)(*(int*)this) + 2));
		((Fun)*((int*)(*(int*)this) + 1))(this);
		((Fun)*((int*)(*(int*)this) + 2))(this);
	}
	
       ~B()
	{
	}
	
        void show() const 
	{
		printf("show b: %d\n", m_b);
	}
	
        void disp() const
	{
		printf("disp b: %d\n", m_b);
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
	A* pob3 = new B(100, 200);
	delete pob3;
	getchar();
	return 0;
}
运行结果如下:

vptr: 00C97860
00C91131
00C910C3
show a: 100
disp a: 100

vptr: 00C9795C
00C9107D
00C9115E
show b: 200
disp b: 200

       从上面可以看出,构造父类的时候父类初始化的虚表指针是指向父类自己的虚表的,而后在构造到子类的时候才会把虚表改为子类的虚表,所以构造函数调用虚函数不会出错,但不会有多态性。



Q:析构函数是否可以调用虚函数?

A:可以,但同样不会具有多态性。

以下是例子分析:

#include <stdio.h>
#include <typeinfo>
using namespace std;
class A;
typedef void(*Fun)(A*);

class A
{
private:
	int m_a;
public:
	A(int a) : m_a(a) 
	{
	}

       virtual ~A()
	{
		printf("%s\n", typeid(*this).name());
		printf("this: %p\n", this);
		printf("vptr: %p\n", *(int*)this);
		((Fun)*((int*)(*(int*)this) + 1))(this);
		((Fun)*((int*)(*(int*)this) + 2))(this);
		printf("\n");
	};
	
        virtual void show() const
	{
		printf("show a: %d\n", m_a);
	}
	
        virtual void disp() const
	{
		printf("disp a: %d\n", m_a);
	}
	
        void T() 
	{
		show();
		disp();
	}
};

class B : public A
{
private:
	int m_b;
public:
	B(int a, int b) : m_b(b), A(a)
	{
	}
	
        ~B()
	{
		printf("%s\n", typeid(*this).name());
		printf("this: %p\n", this);
		printf("vptr: %p\n", *(int*)this);
		((Fun)*((int*)(*(int*)this) + 1))(this);
		((Fun)*((int*)(*(int*)this) + 2))(this);
		printf("\n");
	}
	
        void show() const 
	{
		printf("show b: %d\n", m_b);
	}
	
        void disp() const
	{
		printf("disp b: %d\n", m_b);
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
	A* pob3 = new B(100, 200);
	delete pob3;
	getchar();
	return 0;
}

运行结果如下:

class B
this: 003CE180
vptr: 00EF78B0
show b: 200
disp b: 200

class A
this: 003CE180
vptr: 00EF7860
show a: 100
disp a: 100

       从以上同样可以看出,在析构A的时候,虚表指针已经从指向类B的虚表变为指向类A的虚表了,所以并不能实现多态性。

      (网友相关解释:编译器的做法是,析构子类完成后,恢复父类对象的的虚函数表,这时子类对象对应的父类对象的虚函数表,已经是父类的虚函数表,此时调用虚函数,就不在和正常虚函数调用一样了,父类对象只能调用自己的虚函数,非虚函数调用方式并无不同。)


Q:析构函数为什么要设为虚函数?

A:在类的继承中,如果有基类指针指向派生类,那么用基类指针delete时,如果不定义成虚函数,派生类中派生的那部分无法析构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值