多态与多态对象模型

      这里简单介绍下什么是多态,多态的构成条件,多态原理以及多态的对象模型。在介绍多态之前,先简单的介绍下什么是虚函数。

虚函数

    类的成员函数前面加virtual关键字,则这个成员函数称为虚函数。

    注:1. 除静态成员函数   2. 内联函数不能定义为虚函数

   虚函数重写:

    当在子类的定义了一个与父类完全相同的虚函数时,则称子类的这个函数重写(也称覆盖)了父类的这个虚函数。

   纯虚函数

   在成员函数的形参后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。

   纯虚函数强制重写虚函数。

多态

    1.定义

       多态就是多形态。

       静态多态就是重载,因为是在编译期决议确定的,所以称为静态多态。

       动态多态就是通过继承重写基类的虚函数实现多态,因为是在运行时决议的,所以称为冬天多态。

        当使用基类的指针或引用调用重写的虚函数时,当指向父类调用的就是父类的虚函数,当指向子类调用的就是子类的虚函数。

     注:普通调用与类型有关,多态调用与对象有关。

    2.条件

        1.虚函数重写  2.父类的指针和引用(指向父类调用父类,指向子类调用子类)

eg:

class Base
{
public :
	virtual void func1()
	{
		cout<<"Base::func1" <<endl;
	}

	virtual void func2()
	{
		cout<<"Base::func2" <<endl;
	}

public:
	int a ;
};

class Derive :public Base
{
public :
	virtual void func1()
	{
		cout<<"Derive::func1" <<endl;
	}

	virtual void func3()
	{
		cout<<"Derive::func3" <<endl;
	}

	virtual void func4()
	{
		cout<<"Derive::func4" <<endl;
	}

public:
	int b ;
};

 

    这是一种运行期多态,即父类指针唯有在程序运行时才能知道所指的真正类型是什么。这种运行期决议,是通过虚函数表来实现的。

注:

      1). 派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。

      2). 在基类中定义类虚函数,在该函数中虚函数始终保持虚函数的特性。

      3).四个默认成员函数,除了析构,都不要定义为虚函数。析构函建议定义为虚函数(能保证正确调用对应虚函数)。

why? 另外析构函数比较特殊,因为派生类的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里是因为编译器做了特殊处理)因为父类的指针可能指向父类对象也可能指向子类对象,为了让指向谁调用谁的析构,所用定义为多态,否则会造成内存泄露。

     4). 静态成员还是那户不能定义为虚函数。

多态对象模型

    使用指针访问虚表

   eg:

class Base
{
public :
	virtual void func1()
	{
		cout<<"Base::func1" <<endl;
	}

	virtual void func2()
	{
		cout<<"Base::func2" <<endl;
	}

public:
	int a ;
};

typedef void(*V_FUNC)();

void PrintVTable(int** vtable)
{
	cout<<"===================================="<<endl;

	printf("虚函数表:%p\n", vtable);
	for (size_t i = 0; vtable[i] != 0; ++i)
	{
		printf("vfunc[%d]:%p->", i, vtable[i]);
		V_FUNC f = (V_FUNC)vtable[i];
		f();
	}
	cout<<"===================================="<<endl;
}

void Test()
{
	Base b;
	PrintVTable((int**)(*((int**)&b)));
}

 

分析:

 

 

单继承对象模型

写一个类继承Base

eg:

class Derive :public Base
{
public :
	virtual void func1()
	{
		cout<<"Derive::func1" <<endl;
	}

	virtual void func3()
	{
		cout<<"Derive::func3" <<endl;
	}

	virtual void func4()
	{
		cout<<"Derive::func4" <<endl;
	}

public:
	int _b ;
};

 

分析:

    在C++对象模型中,对于一般继承(这个一般是相对于虚拟继承而言),若子类重写(overwrite)了父类的虚函数,则子类虚函数将覆盖虚表中对应的父类虚函数(注意子类与父类拥有各自的一个虚函数表);若子类并无overwrite父类虚函数,而是声明了自己新的虚函数,则该虚函数地址将扩充到虚函数表最后(在vs中无法通过监视看到扩充的结果,不过我们通过取地址的方法可以做到,子类新的虚函数确实在父类子物体的虚函数表末端)。

  多继承对象模型(非菱形继承)

eg:

class Base1
{
public :
	virtual void func1()
	{
		cout<<"Base1::func1" <<endl;
	}

	virtual void func2()
	{
		cout<<"Base1::func2" <<endl;
	}

private :
	int b1 ;
};

class Base2
{
public :
	virtual void func1()
	{
		cout<<"Base2::func1" <<endl;
	}

	virtual void func2()
	{
		cout<<"Base2::func2" <<endl;
	}

private :
	int b2 ;
};


class Derive : public Base1, public Base2
{
public :
	virtual void func1()
	{
		cout<<"Derive::func1" <<endl;
	}

	virtual void func3()
	{
		cout<<"Derive::func3" <<endl;
	}

private :
	int d1 ;
};


typedef void(*V_FUNC)();

void PrintVTable(int** vtable)
{
	cout<<"===================================="<<endl;

	printf("虚函数表:%p\n", vtable);
	for (size_t i = 0; vtable[i] != 0; ++i)
	{
		printf("vfunc[%d]:%p->", i, vtable[i]);
		V_FUNC f = (V_FUNC)vtable[i];
		f();
	}
	cout<<"===================================="<<endl;
}

void Test()
{
	Derive d;
	PrintVTable((int**)(*((int**)&d)));
	PrintVTable((int**)(*(int**)((char*)&d+sizeof(Base1))));

}

 

分析:

 

    单继承中(一般继承),子类会扩展父类的虚函数表。在多继承中,子类的虚函数被放在声明的第一个基类的虚函数表中。

    注:

      1.C++中,编译时多态主要是通过函数重载和运算符重载实现的。运行时多态性主要通过虚函数重写实现。

      2.C++动态决议的虚拟机制中,使用的vtable就是一个用来保存虚成员函数的地址的函数指针数组。

      3. 同类型的对象虚表相同(共享同一块),拷贝时只拷贝成员变量不拷贝虚表。

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值