虚函数

一、先来了解一下向上类型转换。

// upcasting.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "iostream"
#include "stdlib.h"
using namespace std;
enum note{	middleC, Csharp, Eflat};
class Instrument
{
public:
	void play(note) const
	{
		cout << "Instrument::play " << endl;
	}

};
class Wind:public Instrument
{
public:
	void play(note) const
	{
		cout << "Wind::play " << endl;
	}
};
void tune(Instrument &i)
{
	i.play(Csharp);
}
int main()
{
	Wind flute;
	tune(flute); **//输出 Instrument::play   只有向上类型转化    派生类--〉基类**
	system("pause");
    return 0;
}

函数tune()接受一个Instrument对象,也可以接受Instrument派生的类的对象。
在main中,Wind对象直接传给tune,在public继承中,在Instrument中的接口,必然在Wind中。Wind向上类型转换中,会使自己的的接口“变窄”。

二、了解一下,虚函数的绑定
C编译只有一种函数调用方式,就是早捆绑。而c++有晚捆绑,动态捆绑,或者运行时的捆绑。同样的例子,使用虚函数后:

// bind-virtual.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "iostream"
#include "stdlib.h"
using namespace std;

enum note { middleC, Csharp, Eflat };

class Instrument
{
public:
	virtual void play(note) const
	{
		cout << "Instrument::play " << endl;
	}

};

class Wind :public Instrument
{
public:
	void play(note) const
	{
		cout << "Wind::play " << endl;
	}

};

void tune(Instrument &i)
{
	i.play(Csharp);
}

int main()
{
	Wind flute;
	tune(flute); //输出 Wind::play   涉及到动态绑定 后面再讲
	system("pause");
	return 0;
}

我们只是在基类的函数play前加了virtual,输出就变成了Wind::play,调用了派生类的接口。
如果一个函数在基类中被申明为virtual,那么在所有的派生类中它都是virtual的,在派生类中virtual函数的重定义通常称为重写(overriding)。

三、虚函数扩展性

// extensible-virtual.cpp : 定义控制台应用程序的入口点。
#include "iostream"
#include "stdlib.h"
using namespace std;

enum note { middleC, Csharp, Eflat };

class Instrument
{
public:
	virtual void play(note) const
	{
		cout << "Instrument::play " << endl;
	}

	virtual char * what() const
	{
		return "Instrument";
	}
	virtual void adjust(int){}

};

class Wind :public Instrument
{
public:
	void play(note) const
	{
		cout << "Wind::play " << endl;
	}
	virtual char * what() const {
		return "Wind";
	}
	virtual void adjust(int) {}
};

class Percussion :public Instrument
{
public:
	void play(note) const
	{
		cout << "Percussion::play " << endl;
	}
	virtual char * what() const {
		return "Percussion";
	}
	virtual void adjust(int) {}
};

class Stringed :public Instrument
{
public:
	void play(note) const
	{
		cout << "Stringed::play " << endl;
	}
	virtual char * what() const {
		return "Stringed";
	}
	virtual void adjust(int) {}
};

class Brass :public Wind
{
public:
	void play(note) const
	{
		cout << "Brass::play " << endl;
	}
	virtual char * what() const {
		return "Brass";
	}

};

class WoodWind :public Wind
{
public:
	void play(note) const
	{
		cout << "WoodWind::play " << endl;
	}
	virtual char * what() const {
		return "WoodWind";
	}

};


void tune(Instrument &i)
{
	i.play(Csharp);
}


void f(Instrument &i)
{
	i.adjust(1);
}

Instrument * A[] = {
	new Wind,
	new Percussion,
	new Stringed,
	new Brass,
};

int main()
{
	Wind flute;
	Percussion drum;
	Stringed violin;
	Brass flugelhorn;
	WoodWind recorder;
	tune(flute); 
	tune(drum);
	tune(violin);
	tune(flugelhorn);
	tune(recorder);

	f(flugelhorn);
	return 0;
}

输出结果为:

Wind::play
Percussion::play
Stringed::play
Brass::play
WoodWind::play

看一下 Instrument * A是的结构,new了四个对象。具体如下:
虚函数
为了验证上图的情况,我们看一下调试时的汇编代码:
虚函数
虚函数
虚函数
上面连续三张图,我们看第三张,先push 1 最后有个mov edx+8,刚好两个void*,前面是pasy()和what(), 我们是在x86上调试的,要是在x64上调试,又有什么不同呢?
虚函数
暂时没看懂64位的寄存器的东西,留个疑问,以后来弄明白。
地具体怎么绑定的,重写的,下一篇博文再仔细讲吧。