多态
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(); 访问自己的成员方法 .
上一篇: Ajax PHP 边学边练 之三 数据库_php技巧
下一篇: 阿里云ECS服务器重置实例登录密码