虚函数(重写)、多态、单继承虚表、多继承虚表

虚函数
虚函数:类的成员函数前加virtual关键字,则称这个成员函数为虚函数;
虚函数重写
虚函数重写:当在子类中定义了一个与父类完全相同的虚函数时,则称子类的虚函数重写(覆盖)了父类的虚函数。子类重写的虚函数可以没有virtual(c++语法坑)。
多态
多态:当使用父类的引用或指针调用重写的虚函数时,当指向父类调用的是父类的虚函数,指向子类调用的是子类的虚函数。

class Person
{
public:
       virtual void BuyTickets()
       {
              cout << "成人买票" << endl;
       }
protected:
       string _name;
};
class Student: public Person
{
public:
       virtual void BuyTickets()  //子类里的virtual可以没有,由于是继承,继承了基类虚函数的属性
       {
              cout << "学生买票" << endl;
       }
protected:
       int _num;
};
// 调用重写的虚函数时,对象必须是基类的指针或引用.
// 函数调用和对象有关,指向谁,调用谁的虚函数。当指向父类调用的就是父类的虚函数,当指向子类调用的就是子类的虚函数
void  Buy1(Person* p) //使用基类的指针
{
       p->BuyTickets();
}
void Buy2(Person &p) //使用基类的引用
{
       p.BuyTickets();
}
void Buy3(Person p) 
{
       p.BuyTickets();
}
int main()
{
       Person p;
       Student s;
       p.BuyTickets();  //普通调用,对象类型是Person,会到Person类里找Buytickets();
       s.BuyTickets(); //普通调用,对象类型是Student,会到S类里找Buytickets();
       //基类是指针:必须要&
       Buy1(&p);  // 成人买票 
       Buy1(&s);  // 学生买票
       //基类是引用
       Buy2(p);   // 成人买票  多态调用:对象指向p
       Buy2(s);   // 学生买票  多态调用:对象指向s
       //基类既不是指针也不是引用
       Buy3(p);   //成人买票
       Buy3(s);   //成人买票
       system("pause");
       return 0;
}

多态调用:具有灵活性,函数调用和对象有关,指向谁,调用谁;
普通调用:和类型有关,是那个类型的对象,就调用谁的虚函数;

虚表指针和虚表(多态机理)

在这里插入图片描述
从监视窗口我们可以看到,基类里变量不只有_name ,还有前4个字节为 _vfptr,_vfptr是虚表指针,虚表指针指向父类虚表,虚表里存的是父类虚函数地址。而派生类继承了基类,同样也有虚表指针,这个虚表指针指向的是派生类虚表,虚表里存放的是派生类虚函数地址。通过这个虚表指针就可以找到基类和派生类该调用的虚函数:
在这里插入图片描述
虚表指针是在调用构造函数初始化的,虚表存放于数据段(静态区或全局区),不能存放于栈和堆。
一个类有一个虚表指针,即一个虚表,但虚表里存放了这个类全部的虚函数地址。如下:

class A
{
public:
	virtual void fun1()
	{

	}
	virtual void fun2()
	{

	}
private:
	int _a;
};
int main()
{
	A a1;
}

在这里插入图片描述

在虚表里虚函数地址存放顺序和在基类中声明顺序一样,如下:先声明fun1,后声明fun2,那么就先存放fun1虚函数地址,后存放fun2虚函数地址。在这里插入图片描述
单继承的虚函数表

class A
{
public:
	virtual void fun1()
	{
		cout << "A::fun1()" << endl;
	}
	virtual void fun2()
	{
		cout << "A::fun2()" << endl;
	}
public:
	int _a;
};
class B : public A
{
	//子类会继承父类的fun1()函数
	virtual void fun2()  //子类func2()会覆盖父类fun2()
	{
		cout << "B::fun2()" << endl;
	}
	virtual void fun3()
	{
		cout << "B::fun3()" << endl;
	}
	void fun4()  //fun4()不是虚函数
	{
		cout << "B::fun3()" << endl;
	}
};
int main()
{
	A a1;
	B b1;
}

在这里插入图片描述
从监视窗口,我们可以看到子类的虚表里有继承了父类的A::fun1( ),还有覆盖的B::fun2( )函数,由于fun4( )不是虚函数,所以虚表里当然没有fun4( ),尽管没有看见子类的fun3虚函数,但是这个函数就在虚表里的fun2()函数后,只是vs监视没有显示出来,可以认为是vs的bug。但我们可以从以下方式看到真实的虚表情况:

typedef void(*FUNC)();   

void printVtable(int *vfter)  
{
	cout << "虚表地址:" << vfter << endl;
	for (int i = 0; vfter[i] != 0; i++) //因为虚表最后一个元素是0
	{
		printf("第%d个虚函数地址:0x%x,->", i, vfter[i]);
		FUNC f = (FUNC)vfter[i]; 
		f(); //调用该虚函数
	}
	cout << endl;
}

int main()
{
	A a1;
	B b1;
	int *vfter1 = (int*)(*(int*)(&a1));  //vfter是虚表地址
	int *vfter2 = (int*)(*(int*)(&b1));
	printVtable(vfter1);
	printVtable(vfter2);
	system("pause");
	return 0;
}

在这里插入图片描述
在这里插入图片描述
多继承虚函数表
首先看多继承代码:

class A
{
	virtual void func1()
	{
		cout << "A::func1()" << endl;
	}
	virtual void func2()
	{
		cout << "A::func2()" << endl;
	}
protected:
	int _a;
};
class B
{
	virtual void func1()
	{
		cout << "B::func1()" << endl;
	}
	virtual void func2()
	{
		cout << "B::func2()" << endl;
	}
protected :
	int _b;
};
class C : public A, public B
{
	virtual void func1()
	{
		cout << "C::func1()" << endl;
	}
	virtual void func3()
	{
		cout << "C::func3()" << endl;
	}
protected :
	int _c;

};
typedef void(*FUNC)(); //声明一个函数指针类型FUNC

void printTable(int *vfptr)
{
	printf("vtable address:%p\n", vfptr);
	for (int i = 0; vfptr[i] != 0; i++)
	{
		printf("第%d个虚函数地址:%p->", i, vfptr[i]);
		FUNC f = (FUNC)(vfptr[i]);
		f();  //调用这个函数
	}
	printf("\n");
}

int main()
{
	C c;
	cout<<sizeof(c)<<endl;
	//因为c有两个直接父类:A和B
	int *vfptr1 = (int*)(*((int*)(&c)));
	printTable(vfptr1);
	//因为第二个虚表指针存放在第一个父类成员变量下边,所以第二个虚表指针在&c后需要加上第一个父类的大小,
	//但是由于指针+1,加的是所指类型的大小,所以需要把&a强转为char*,或者+sizeof(A)/4,
	int *vfptr2 = (int*)(*(int*)(((char*)(&c)) + sizeof(A))); 
	printTable(vfptr2);
	system("pause");
	return 0;
}

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/sophia__yu/article/details/82791592
今日推荐