多态中虚函数的简单理解

虚函数

在之前,我曾经在多态中简单的提到过虚函数,虚函数就是用virtual关键字去修饰类的成员函数,然后将这个函数在派生类中去重新实现。

以代码为例:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>

using namespace std;


class B
{
public:
	virtual void Test1(int i)
	{
		cout << "B:Test1()" << endl;
	}
	virtual void Test2(int i)
	{
		cout << "B:Test2()" << endl;
	}
	void Test3(int i)
	{
		cout << "B:Test3()" << endl;
	}
};

class C: public B
{
public:
	virtual void Test1(int i)
	{
		cout << "C:Test1()" << endl;
	}
	void Test2(int i)
	{
		cout << "C:Test2()" << endl;
	}
	virtual void Test3(int i)
	{
		cout << "C:Test3()" << endl;
	}
};

int main()
{
	B* b = new C;
	b->Test1(0);
	b->Test2(0);
	b->Test3(0);
	return 0;
}

其中Test1就是进行了重写的虚函数,而Test2没有进行重写,Test3则是不是虚函数,所以这里的Test2和Test3都没有实现动态多态,只有Test1实现了动态多态。

之前强调过的我在这里再强调一遍,动态多态有两个条件

①必须是虚函数;②必须通过基类类型的指针或引用进行调用

纯虚函数

介绍了虚函数,那么就得提到纯虚函数。纯虚函数就是在成员函数的形参后面写上=0,那么成员函数就是纯虚函数。而包含纯虚函数的类就叫做抽象类(又叫接口类)。但是需要注意的是, 抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象

上面说了虚函数,大体的解释了一下虚函数的使用方式,但是,在类中使用虚函数的时候,有一些注意事项你还是需要注意的:

①派生类重写基类中的虚函数实现多态,函数名,参数列表,返回值全都要一样。(协变除外,协变是返回值不一样);

②派生类中重写的虚函数要始终保持与基类中虚函数的特性一致;

③只有类中的非静态成员函数才能定义为虚函数,静态成员函数不能定义为虚函数;

④构造函数不能定义为虚函数,但是最好将基类的析构函数声明为虚函数,另外operate=定义为虚函数也是可以的,但是最好不要这样做,容易导致混淆;

⑤不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会出现未定义的行为;

⑥如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加;

⑦虚表是所有类对象实例共用的。

虚表

既然在上面的注意事项中写到了虚表,那么在这里就得说一下虚表并进行虚表剖析。

对于有虚函数的类,编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针

以代码为例:

class Test
{
public:
	Test()
	{
		i = 10;
		cout << "this = " << this << endl;
	}
	virtual ~Test()
	{}
private:
	int i;
};

int main()
{
	Test t;
	cout << sizeof(t) << endl;
	return 0;
}

通过这个图片我们可以发现,在创建带有虚函数的类时,编译器会帮助维护一张虚表,其中 对象的前四个字节就是指向虚表的指针

下面我们将分析没有覆盖和有覆盖两种情况。

没有覆盖:

代码:

class Base
{
public:
	Base(){ i = 10; }
	virtual void FunTest0(){ cout << "Base::FunTest0()"; }
	virtual void FunTest1(){ cout << "Base::FunTest1()"; }
	virtual void FunTest2(){ cout << "Base::FunTest2()"; }
private:
	int i;
};
class Derived :public Base
{
public:
	virtual void FunTest4(){ cout << "Derived::FunTest4()"; }
	virtual void FunTest5(){ cout << "Derived::FunTest5()"; }
	virtual void FunTest6(){ cout << "Derived::FunTest6()"; }
};

typedef void(*FUN_TEST)();

void FunTest()
{
	Base b;
	cout << "Base vfptr:" << endl;
	for (int i = 0; i < 3; ++i)
	{
		FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&b + i));
		funTest();
		cout << ": " << (int *)funTest << endl;
	}
	cout << endl;
	Derived d;
	cout << "Derived vfptr:" << endl;
	for (int i = 0; i < 6; ++i)
	{
		FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&d + i));
		funTest();
		cout << ": " << (int *)funTest << endl;
	}
}

int main()
{
	FunTest();
	return 0;
}

由代码进行分析:

有覆盖:

代码:

class Base
{
public:
	virtual void FunTest0(){ cout << "Base::FunTest0()" << endl; }
	virtual void FunTest1(){ cout << "Base::FunTest1()" << endl; }
	virtual void FunTest2(){ cout << "Base::FunTest2()" << endl; }
	virtual void FunTest3(){ cout << "Base::FunTest3()" << endl; }
};
class Derived :public Base
{
public:
	virtual void FunTest0(){ cout << "Derived::FunTest0()" << endl; }
	virtual void FunTest1(){ cout << "Derived::FunTest1()" << endl; }
	virtual void FunTest4(){ cout << "Derived::FunTest4()" << endl; }
	virtual void FunTest5(){ cout << "Derived::FunTest5()" << endl; }
};

typedef void(*_pFunTest)();

void FunTest()
{
	Base base;
	for (int i = 0; i < 4; ++i)
	{
		_pFunTest  pFunTest = (_pFunTest)(*((int*)*(int *)&base + i));
		pFunTest();
	}
	cout << endl;
	Derived derived;
	for (int i = 0; i < 6; ++i)
	{
		_pFunTest  pFunTest = (_pFunTest)(*((int*)*(int *)&derived + i));
		pFunTest();
	}
}
void TestVirtual()
{
	Base base0;
	Derived derived;
	Base& base1 = derived;
}
int main()
{
	FunTest();
	TestVirtual();
	return 0;
}
由代码进行分析:



下面我再简单剖析一下多重继承中有无虚函数覆盖的情况:

多重继承:没有虚函数覆盖

代码:

class Base0
{
public:
	Base0()
	{ 
		i = 0xB0; 
	}
	virtual void PrintB0()
	{ 
		cout << "i = " << hex << i << "  Base0::PrintB0()" << endl;
	}
	int i;
};
class Base1
{
public:
	Base1()
	{ 
		i = 0xB1;
	}
	virtual void PrintB1()
	{ 
		cout << "i = " << hex << i << "  Base1::PrintB1()" << endl; 
	}
	int i;
};
class Base2
{
public:          
	Base2()
	{ 
		i = 0xB2; 
	}
	virtual void PrintB2()
	{ 
		cout << "i = " << hex << i << "  Base2::PrintB2()" << endl;
	}
	int i;
};
class Derived :public Base0, public Base1, public Base2
{
public:
	Derived()
	{ 
		i = 0xD0;
	}
	virtual void PrintD()
	{ 
		cout << "i = " << hex << i << "  Derived::PrintD()" << endl;
	}
	int i;
};

typedef void(*VFTABLE_FUN)();

void PrintVfPTab(char * _pStr, int *_pVfAddr)
{
	cout << _pStr << endl;
	for (int i = 0;; i++)
	{
		VFTABLE_FUN pPrintVTab = (VFTABLE_FUN)(*(_pVfAddr + i));
		if (NULL == pPrintVTab)
		{
			break;
		}
		pPrintVTab();
		cout << (int*)pPrintVTab << endl;
	}
	cout << endl;
}
void FunTest()
{
	Derived derived;
	cout << sizeof(derived) << endl;
	int *pVfTAddr = NULL;

	Base0& base0 = derived;
	pVfTAddr = (int*)(*(int *)&base0);
	PrintVfPTab("Base0 virtual Tab:", pVfTAddr);

	Base1& base1 = derived;
	pVfTAddr = (int*)(*(int *)&base1);
	PrintVfPTab("Base1 virtual Tab:", pVfTAddr);

	Base2& base2 = derived;
	pVfTAddr = (int*)(*(int *)&base2);
	PrintVfPTab("Base2 virtual Tab:", pVfTAddr);

	pVfTAddr = (int*)(*(int *)&derived);
	PrintVfPTab("Derived virtual Tab:", pVfTAddr);

	derived.PrintB0();
	derived.PrintB1();
	derived.PrintB2();
	derived.PrintD();
}

int main()
{
	FunTest();
	return 0;
}
由代码进行分析:


多重继承:有虚函数覆盖

代码:

class Base0
{
public:
	Base0(){ i = 0xA0; }
	virtual void Print(){ cout << "i = " << hex << i << "  Base2::Print()" << endl; }
	int i;
};
class Base1
{
public:
	Base1(){ i = 0xB0; }
	virtual void Print(){ cout << "i = " << hex << i << "  Base2::Print()" << endl; }
	int i;
};
class Base2{
public:
	Base2(){ i = 0xC0; }
	virtual void Print(){ cout << "i = " << hex << i << "  Base2::Print()" << endl; }
	int i;
};
class Derived :public Base0, public Base1, public Base2
{
public:
	Derived(){ i = 0xD0; }
	virtual void Print(){ cout << "i = " << hex << i << "  Derived::Print()" << endl; }
	int i;
};
void FunTest()
{
	Derived derived;
	cout << sizeof(derived) << endl;
	Base0& base0 = derived;
	base0.Print();
	Base1& base1 = derived;
	base1.Print();
	Base2& base2 = derived;
	base2.Print();
	derived.Print();
}
由代码进行分析:



注:以上代码全是放在VS2013下进行调试的。













  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值