欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

多态

程序员文章站 2022-05-13 19:14:49
...

C++的三大特点:封装,继承,多态
多态:给某一对象发送相同消息时,表现不同的形态。
  发送消息:  对象.成员方法()
  
多态分类:静态多态,动态多态
   静态多态(静态联编):在编译时就已经确定调用的方法(重载函数,重载运算符)
   动态多态(动态联编):在编译时无法确定调用的方法(虚函数)
  
虚函数:
  目的:通过基类指针可以指向子类的同名方法。实现动态多态  
   定义:在父类中修饰
    class base
    {
    ...
        virtual 声明 函数名;
    ...
    };
    注:不能出现在类外定义中,只能在类中声明
    本质:是通过在对象中分配一个虚函数表(数组)
    
变量:
 定义变量: 类型 变量名;
 定义指针变量: 
            数据变量:    类型* 变量名;
             函数指针变量:  返回类型 (*指针变量)(列参类型1,...);

             
纯虚函数:只给出声明但不给出定义的函数
                virtual 返回类型 函数名(..)=0
                
抽象类:含有纯虚函数的类                
    注:1、抽象类不能实例化。()        
       2、抽象类能定义属性
       3、抽象类中能定义非纯虚函数
       4、纯虚方法只能由子类实现

 

   静态多态重载运算符的实现 : 

#include<iostream>
using namespace std;
//多态:(给一个对象发送消息表现出不同形态)
class String
{
public:
	//构造器重载---重载函数
	String()
	{
		str[0]='\0';
	}
	String(const char* s)
	{
		int i=0;
		while((str[i]=s[i])!='\0')
			i++;
	}
	//重载运算符(重载函数):返回类型  operator<运算符>()   +
	String operator+(const char* s)
	{
		String result;
		int i=0,j=0;
		while('\0'!=(result.str[i]=this->str[i])) i++;//将this赋值给s
		while(result.str[i]=s[j]) 
		{
			i++;
			j++;
		}
		return result;
	}
	String operator+(String& s)
	{
		int i=0,j=0;
		String result;
		while(0!=(result.str[i]=this->str[i]))i++;
		while('\0'!=(result.str[i]=s.str[j]))
		{
			i++;
			j++;
		}
		return result;
	}
	String operator+(char ch)
	{
		String result;
		int i=0;
		while('\0'!=(result.str[i]=this->str[i]))i++;
		result.str[i]=ch;
		result.str[i+1]='\0';
		return result;
	}
private:
	char str[100];//abcd1234
};

int main()
{
//重载函数:构造器
	String s;//无参    s.String 
	String s1("abcde");	//s.String   有参
//重载运算符:+
	//1+1   1+3.14   1+'a'	
	s.operator+("abcd");//s+"abcd";	//--->    s1.operator+("abcde")
	s.operator+(s);//s+s1;//1+1
	s.operator+(100);//s+'a';	
	//string a;
	//s.operator+(a);	//重载运算符:静态多态
}

 

   动态多态 : 

#include<iostream>
using namespace std;

class base
{
public:
	virtual void showa()
	{
		cout<<"base::a"<<endl;
	}
	virtual void showb()
	{
		cout<<"base::b"<<endl;
	}
	//与子类中displaya同名
	virtual void displaya()
	{
		cout<<"base::display_a"<<endl;
	}
	virtual void showc()
	{
		cout<<"base::c"<<endl;
	}
};
class people//-------------------------------->注释3
{
public:
	virtual void lista()
	{
		cout<<"people::lista"<<endl;
	}
	virtual void displayb()
	{
		cout<<"people::displayb"<<endl;
	}
};

//多继承  
class famer:public base ,public people
{
public:
	virtual void displaya()
	{
		cout<<"famer::a"<<endl;
	}
	virtual void displayb()
	{
		cout<<"famer::b"<<endl;
	}
};


int main()
{
	famer f;//--------------------------->注释2
	cout<<sizeof(f)<<endl;	

	typedef void(*Fun)(void);
	Fun pfun;//指针变量

	//表第1个元素的内容
	pfun=(Fun)(*(int*)*(int*)&f);
	pfun();

	//表第2个元素的内容
	pfun=(Fun)(*(((int*)*(int*)&f)+1));
	pfun();
	//表第3个元素的内容
	pfun=(Fun)(*(((int*)*(int*)&f)+2));
	pfun();
	//表第4个元素的内容
	pfun=(Fun)(*(((int*)*(int*)&f)+3));
	pfun();

	cout<<endl;
	//表2-1
	pfun=(Fun)*((int*)(*((int*)&f+1)));
	pfun();
	pfun=(Fun)  *(int*)(*(((int*)&f)+1)+1);
	pfun();
	
/*
	famer f;
	cout<<sizeof(f)<<endl;
	//重命名
	typedef void(*Fun)(void);
	Fun pfun;//指针变量
	//表第1个元素的内容
	pfun=(Fun)(*(int*)*(int*)&f);
	pfun();
	//表第2个元素的内容
	pfun=(Fun)(*(((int*)*(int*)&f)+1));
	pfun();
	//表第3个元素的内容
	pfun=(Fun)(*(((int*)*(int*)&f)+2));
	pfun();
	//表第4个元素的内容
	pfun=(Fun)(*(((int*)*(int*)&f)+3));
	pfun();
	//表第5个元素的内容
	pfun=(Fun)(*(((int*)*(int*)&f)+4));
	pfun();
	//表第6个元素的内容
	//pfun=(Fun)(*(((int*)*(int*)&f)+5));
	//pfun();
	
	base* p=&f;
	p->displaya();//取出子类
	p->base::displaya();//取出父类方法

*/	

/*
//实例化
	base b;//----------------------------------->注释1
	cout<<sizeof(b)<<endl;

	//取出地址:表地址
	cout<<*(int*)&b<<endl;
    //取出表空间中第一个元素的内容---函数的入口地址
	cout<<*((int*)*(int*)&b)<<endl;
	//取出表空间中第二个元素的内容---函数的入口地址
	cout<<*(((int*)*(int*)&b)+1)<<endl;
	//取函数指针类型 别名
	typedef void (*Fun)(void);
	Fun pfun;//定义指针变量
	pfun=(Fun)*(((int*)(*(int*)&b))+2);
	pfun();
	b.showa();
*/
}

     

多态

   注释1 : 开始实例化了一个 base  b ; b占了四个字节 , 代表虚函数表的地址 , 前三个代表base类里面的虚函数的三个showX的成员地址 , 最后一个代表结束的.

    //实例化
    base b;
    cout<<sizeof(b)<<endl;
    //取出地址:表地址
    cout<<*(int*)&b<<endl;
    //取出表空间中第一个元素的内容---函数的入口地址
    cout<<*((int*)*(int*)&b)<<endl;

   取出表的地址 , 强制类型转换 &b , 就取到了表的地址 , 虚函数表的内容就是元素的地址 , 虚函数表里面还有四个成员的地址 , 要取到虚函数成员的元素内容 , 则是  //取出表空间中第一个元素的内容---函数的入口地址  cout<<*((int*)*(int*)&b)<<endl; (因为表不一定是占四个自己的,按64位的则是占8个字节的 , 所以取内容则还要加一个(int*) 转换一次 )  , 首元素取出来了 , 取第二个元素则      //取出表空间中第二个元素的内容---函数的入口地址         cout<<*(((int*)*(int*)&b)+1)<<endl; 

//取函数指针类型 别名
	typedef void (*Fun)(void);
	Fun pfun;//定义指针变量
	pfun=(Fun)*(((int*)(*(int*)&b))+2);
	pfun();
	b.showa();

    然后定义了一个函数指针 , 定义函数指针变量 , 去存储函数入口地址 , 先取  (int*)&b  的地址 ,  然后  *(int*)&b  再取内容,内容为表的首地址,  然后就可以取出 virtual void showc() 函数的函数入口地址   pfun=(Fun)*(((int*)(*(int*)&b))+2);  从而virtual(虚函数) 就可以通过基类指向子类的方法了 . 

   注释2 : 首先先实例化一下  famer f ;   cout<<sizeof(f)<<endl;  发现famer的成员也占4个字节 , 所以famer也有了一个虚函数表的地址 , 当famer继承了base类  class famer:public base  发现  cout<<sizeof(f)<<endl;  成员 f 还是四个字节 , 所以它们两个类都在一个虚函数表的空间里面 了.

多态

根据上图可以看出 , famer:base 后在同一个虚函数表里 , 所以虚函数表里面有 famer 的三个showX成员 和 base 的两个displayX成员 , 还有一个结束 , 但是到底是base的成员在表的前面 , 还是base的成员在后面呢?

	famer f;
	cout<<sizeof(f)<<endl;
	//重命名
	typedef void(*Fun)(void);
	Fun pfun;//指针变量
	//表第1个元素的内容
	pfun=(Fun)(*(int*)*(int*)&f);
	pfun();
	//表第2个元素的内容
	pfun=(Fun)(*(((int*)*(int*)&f)+1));
	pfun();
	//表第3个元素的内容
	pfun=(Fun)(*(((int*)*(int*)&f)+2));
	pfun();
	//表第4个元素的内容
	pfun=(Fun)(*(((int*)*(int*)&f)+3));
	pfun();
	//表第5个元素的内容
	pfun=(Fun)(*(((int*)*(int*)&f)+4));
	pfun();
	//表第6个元素的内容
	//pfun=(Fun)(*(((int*)*(int*)&f)+5));
	//pfun();

多态

 

 

 

我们取出虚函数表元素的内容 , 发现 : base元素在前面 .

	base* p=&f;
	p->displaya();//取出子类
	p->base::displaya();//取出父类方法

多态

当我们用  base * p=&f  基类指针指向子类的之后 , 但是还是不能够指向子类的displayX()的方法 , 因为base类里面没有displayX()的方法 .

 

 

为了可以让基类的成员可以指向子类的成员 , 我们可以在基类里面添加一个虚方法 . 

//与子类中displaya同名
	virtual void displaya()
	{
		cout<<"base::display_a"<<endl;
	}

所以在基类中添加了一个虚方法 . 多态

当基类中有子类的同名虚函数时,子类的a方法会替换基类中和子类同名的虚方法,所以famer的成员b和NULL会向前移动 , 但是只是地址替换,基类虚函数还在虚函数表的外面 .

    base* p=&f;
    p->displaya();//取出子类
    p->base::displaya();//通过base::取出父类方法

所以还是可以通过base::作用域来取出 基类的displaya()的成员方法 . 

 

  注释 3 :  再写一个people 的类 , 多态让famer继承两个类一个people 一个 base , 所以此时people 也会有一个虚函数表 .

class people
{
public:
	virtual void lista()
	{
		cout<<"people::lista"<<endl;
	}
	virtual void displayb()
	{
		cout<<"people::displayb"<<endl;
	}
};

当people类里面也有一个 displayb() 的成员方法时 , 和famer 的成员方法一样 , 所以子类famer的 displayb()成员方法就会替换掉people的displayb() 成员方法的地址 , 但是people的displayb() 成员方法的作用域还在 , 可以通过 people  p;  p->displayb(); 访问自己的成员方法 . 

 

相关标签: 原创