C++之多态

来源:互联网 发布:存储过程拼接sql语句 编辑:程序博客网 时间:2024/06/09 18:03
概念:多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。
多态类别
(1)静态多态:静态多态通过函数重载和泛型编程实现,编译器在编译期间完成。
(2)动态多态:(也叫动态绑定)动态多态则通过虚函数来实现,在程序执行期间进行。被关键字virtual修饰的成员函数叫虚函数,当基类中有虚函数时,派生类要重写虚函数,以此来实现动态 绑定。
#include<iostream>
#include<stdlib.h>
using namespace std;
class Base
{
public:
 virtual void funtest1()
 {
  cout << "funtest1" << endl;
 }
   int  data = 9;
};
int main()
{
 Base b;
 cout << sizeof(b) << endl;
 system("pause");
 return 0;
}
 
我们可以看到,大小为8,那么里面除了一个int类型的数据外,就剩下一个虚函数了,都知道我们的int在32位平台下是4个字节,那么剩下的四个字节是什么呢?然后我们调试在取对象的地址跟一步看看
 
在里面我们可以看到我们的数据9,9的上面看起来像个地址,那他是不是指针呢,我们在上个实验的代码上在加两个虚函数,一个普通的成员函数看看
#include<iostream>
#include<stdlib.h>
using namespace std;
class Base
{
public:
 virtual void funtest1()
 {
  cout << "funtest1" << endl;
 }
 virtual void funtest2()
 {
  cout << "funtest2" << endl;
 }
 virtual void funtest3()
 {
  cout << "funtest3" << endl;
 }
 void funtest4()
 {
  cout << "funtest4" << endl;
 }
 int _data = 9;
};
int main()
{
 Base b;
 cout << sizeof(b) << endl;
 system("pause");
 return 0;
}
运行结果依旧为8,然后我们继续调试跟进去看看


可以看到此时我们有三个虚函数了,我们取对象的地址后,在跟一步得到上图,我们三个虚函数恰好三个指针,是这样的吗?我们继续进行研究。
#include<iostream>
#include<stdlib.h>
using namespace std;
class Base
{
public:
 virtual void funtest1()
 {
  cout << "funtest1" << endl;
 }
 virtual void funtest2()
 {
  cout << "funtest2" << endl;
 }
 virtual void funtest3()
 {
  cout << "funtest3" << endl;
 }
 void funtest4()
 {
  cout << "funtest4" << endl;
 }
 int _data = 9;
};
typedef   void(*POINT)();//定义一个函数指针
void print()
{
 Base b;
 POINT *p = (POINT*)(*(int*)&b);
 while (*p)
 {
  (*p)();
  ++p;
 }
}
int main()
{
 print();
 system("pause");
 return 0;
}

我们定义一个函数指针类型,然后让他++,结果可以看到,我们看的p的值就正是表的地址,指向那块表。我们把这块表叫虚表。

可见,这个指针指向了一块虚表,而虚表里的东西又指向了每个虚函数,在这个测试的时候我们仅仅是给了一个类去看的,我们下来给一个派生类去公有继承这个类,但并不给派生类里面放东西,也不重写基类的虚函数,然后看看对象模型是怎么样的。
#include<iostream>
#include<stdlib.h>
using namespace std;
class Base
{
public:
 virtual void funtest1()
 {
  cout << "Base:funtest1" << endl;
 }
 virtual void funtest2()
 {
  cout << "Base:funtest2" << endl;
 }
 virtual void funtest3()
 {
  cout << "Base:funtest3" << endl;
 }
 void funtest4()
 {
  cout << "Base:funtest4" << endl;
 }
 int _data = 9;
};
class Dervied :public Base
{
};
typedef   void(*POINT)();//定义一个函数指针
void print()
{
 Base b;
 cout << "Base:" << endl;
 POINT *p = (POINT*)(*(int*)&b);
 while (*p)
 {
  (*p)();
  ++p;
 }
 Dervied d;
 cout << " Dervied:" << endl;
 POINT *q = (POINT*)(*(int*)&d)
 
int main()
{
 print();
 system("pause");
 return 0;
}
我们反汇编一下,看一下派生类的构造函数做了什么
结论:可以看到在没有重写虚函数且派生类里面也没有放东西的情况下,派生类的构造函数调用了基类的构造函数。此时派生类和基类公用一张虚表。
下面我们只部分的重写基类的虚函数,查看结果
#include<iostream>
#include<stdlib.h>
using namespace std;
class Base
{
public:
 virtual void funtest1()
 {
  cout << "Base:funtest1" << endl;
 }
 virtual void funtest2()
 {
  cout << "Base:funtest2" << endl;
 }
 virtual void funtest3()
 {
  cout << "Base:funtest3" << endl;
 }
 void funtest4()
 {
  cout << "Base:funtest4" << endl;
 }
 int _data = 9;
};
class Dervied :public Base
{
public:
  void funtest1()
 {
  cout << "Dervied:funtest1" << endl;
 }
    void funtest2()
 {
  cout << "Dervied:funtest2" << endl;
 }
 virtual void  funtest5()
 {
  cout << "Dervied:funtest5" << endl;
 }
};
typedef   void(*POINT)();//定义一个函数指针
void print()
{
 Base b;
 cout << "Base:" << endl;
 POINT *p = (POINT*)(*(int*)&b);
 while (*p)
 {
  (*p)();
  ++p;
 }
 cout << " Dervied:" << endl;
 POINT *q = (POINT*)(*(int*)&d);
 while (*q)
 {
  (*q)();
  ++q;
 }
}
void print1()
{
 Dervied d;
 cout << " Dervied:" << endl;
 POINT *p = (POINT*)(*(int*)&d);
 while (*p)
 {
  (*p)();
  ++p;
 }
}
int main()
{
 print();
 print1();
 system("pause");
 return 0;
}


结论:我们公有继承了基类,一般来说入股基类有虚函数,派生类就一定要重写虚函数,而我在写这个派生类的时候,只重写写了两个虚函数。我们可以看到在派生类中没有重写的虚函数,编译器还是给我们打印了出来;
对派生类而言整个顺序就是(1)先拷基类的虚表
                                           (2)如果重写了虚函数,就替换基类相同位置上的虚函数
                                           (3)最后加上派生类自己的虚函数
虚函数的调用:(1)先通过虚表指针找到虚表
                         (2)然后在虚表里找到函数所在位置。
上面我们说了单继承的虚函数,现在我们一起来看一下多继承的虚函数,
#include<iostream>
#include<stdlib.h>
using   namespace  std;
class   Base1
{
public:
    virtual  void  funtest0()
 {
      cout << "Base1:funtest0" << endl;
 }
 virtual   void  funtest1()
 {
      cout << "Base1:funtest1" << endl;
 }
 virtual void funtest2()
 {
       cout << "Base1:funtest2" << endl;
 }
 int  a1  =  9;
};
class  Base2
{
public:
   virtual void funtest3()
 {
      cout << "Base2:funtest3" << endl;
 }
 virtual void funtest4()
 {
      cout << "Base2:funtest4" << endl;
 }
 int a2 = 10;
};
class Dervied :public Base1,public Base2
{
public:
 virtual void  funtest1()
 {
     cout << "Dervied:funtest1" << endl;
 }
 virtual void funtest3()
 {
     cout << "Dervied:funtest3" << endl;
 }
   virtual void funtest5()
 {
    cout << "Dervied:funtest5" << endl;
 }
};
typedef   void(*POINT)();//定义一个函数指针
void print()
{
 Dervied d;
 cout << "Dervied:" << endl;
 POINT *p = (POINT*)(*(int*)&d);
 while (*p)
 {
  (*p)();
  ++p;
 }
 Base2& b = d;
  cout <<endl;
 POINT *q = (POINT*)(*(int*)&b);
 while (*q)
 {
  (*q)();
  ++q;
 }
}
 
int main()
{
 print();
 system("pause");
 return 0;
}

运行结果:


我们来分析一下,求出派生类的大小为20,派生类d公有继承自b1和b2,所以此时的d中就有了两个虚表,根据打印的虚表我们可以看出,我们派生类自己的虚函数跟在了第一张虚表上,或者说他属于第一张虚表。








0 0