C++里的继承和多态(下)——单继承、多继承、菱形继承(含虚拟函数的继承)

1、带有虚函数的类

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

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

                 int _data1;
};
int main()
{
                 Base b;
                b._data1 = 0x01;
                 return 0;
}

对象模型:

mian函数的反汇编:

如果类中没有自己写构造函数,并且含有虚函数,那么编译器就会合成一个缺省的构造函数。

Base()的构造函数分析:

1>在它的构造函数中主要完成的是:在&b指向的那块空间中,填写了虚指针,

2>注意:虚表是编译器在编译和链接完成之后就已经建立好的,在构造函数中只是将虚表的地址填写到对象的前4个字节。

3>虚表中虚函数地址的存放顺序,是按照它在类中声明的顺序存放的。

4>虚表中最后的00 00 00 00 表示虚表结束(编译器不同就不一定了)

vptr指向虚表:

对象模型:

先是虚函数指针,再是该类的数据成员

2、单继承(派生类中没有虚函数的覆盖)

class Base
{
public:
virtual void FunTest1()
{cout<<"Base::FunTest1()"<<endl;}
 
virtual void FunTest2()
{cout<<"Base::FunTest2()"<<endl;}
           int _data1;
};
class Derive:public Base
{
public:
virtual void FunTest3()
{cout<<"Derive::FunTest3()"<<endl;}
 
virtual void FunTest4()
{cout<<"Derive::FunTest4()"<<endl;}
 int _data2;
}; 
// 打印虚表
typedef void (*VtbFun)();
void PrintVtable()
{
cout<<"Derive类的虚函数表: "<<endl;
Derive d1;
d1._data1 = 0x01;
d1._data2 = 0x02;
 
int* pVTable = (int*)*(int*)&d1;
VtbFun FunTest = (VtbFun)*pVTable;
while(NULL != FunTest)
{
FunTest();
cout<<(int*)FunTest<<endl;
pVTable += 1;
FunTest = (VtbFun)*pVTable;
}
cout<<"虚表结束: "<<endl;
}
int main()
{
Base b1;
Derive d1;
return 0;
}

主要还是完成虚指针的填写

在是Derive的构造函数:

派生类最后的对象模型为:如果派生类没有对基类中的虚函数进行重写时,派生类中的虚表先是基类的虚函数地址,然后再加上自己的虚函数地址。虚函数地址的顺序为在类中声明的顺序。

派生类中Base的虚表

3、单继承(含有虚函数的覆盖)

class Base
{
public:
                 virtual void FunTest1()
                {
                                cout << "Base::FunTest1()" << endl;
                }
                 virtual void FunTest2()
                {
                                cout << "Base::FunTest2()" << endl;
                }
                 int _data1;
};
class Derive :public Base
{
public:
                 virtual void FunTest1()//覆盖基类中的FunTest1
                {
                                cout << "Derive::FunTest1()" << endl;
                }
                 virtual void FunTest3()
                {
                                cout << "Derive::FunTest3()" << endl;
                }
                 virtual void FunTest4()
                {
                                cout << "Derive::FunTest4()" << endl;
                }
                 int _data2;
};
typedef void (*VtbFun)();
void PrintVtable()
{
                cout << "Derive类的虚函数表: " << endl;
                 Derive d1;
                d1._data1 = 0x01;
                d1._data2 = 0x02;

                 int* pVTable = (int *)*(int*)&d1;
                 VtbFun FunTest = (VtbFun )*pVTable;
                 while (NULL != FunTest)
                {
                                FunTest();
                                cout << ( int*)FunTest << endl;
                                pVTable += 1;
                                FunTest = ( VtbFun)*pVTable;
                }
                cout << "虚表结束: " << endl;
}
int main()
{
                PrintVtable();
                 return 0;
}

派生类对象模型及虚表建立规则:先将基类的虚表搬移过来,若派生类对基类中的某个函数进行了重写,则会用派生类重写的虚函数的地址替换掉虚表中相应位置基类中虚函数的地址,替换完之后再将派生类自己的虚函数地址按照声明的顺序添加到虚表中

4、多继承(没有虚函数的覆盖)

class Base
{
public:
                 virtual void FunTest1()
                {
                                cout << "Base::FunTest1()" << endl;
                }
                 virtual void FunTest2()
                {
                                cout << "Base::FunTest2()" << endl;
                }
                 int _data1;
};
class Base1
{
public:
                 virtual void FunTest3()
                {
                                cout << "Base1::FunTest3()" << endl;
                }
                 virtual void FunTest4()
                {
                                cout << "Base1::FunTest4()" << endl;
                }
                 int _data2;
};
class Derive :public Base, public Base1
{
public:
                 virtual void FunTest5()
                {
                                cout << "Derive::FunTest5()" << endl;
                }
                 int _data3;
};

typedef void (*VtbFun)();
void PrintVtable()
{
                cout << "Derive类的虚函数表: " << endl;
                 Derive d1;
                d1._data1 = 0x01;
                d1._data2 = 0x02;

                 int* pVTable = (int *)*(int*)&d1;
                 VtbFun FunTest = (VtbFun )*pVTable;
                 while (NULL != FunTest)
                {
                                FunTest();
                                cout << ( int*)FunTest << endl;
                                pVTable += 1;
                                FunTest = ( VtbFun)*pVTable;
                }
                cout << "虚表结束. " << endl;
}
int main()
{
                 Derive d;
                d._data1 = 0x01;
                d._data2 = 0x02;
                d._data3 = 0x03;
                PrintVtable();
                 return 0;
}

5、多继承(含有虚函数的覆盖)

class Base
{
public:
                 virtual void FunTest1()
                {
                                cout << "Base::FunTest1()" << endl;
                }
                 virtual void FunTest2()
                {
                                cout << "Base::FunTest2()" << endl;
                }
                 int _data1;
};
class Base1
{
public:
                 virtual void FunTest3()
                {
                                cout << "Base1::FunTest3()" << endl;
                }
                 virtual void FunTest4()
                {
                                cout << "Base1::FunTest4()" << endl;
                }
                 int _data2;
};
// 这次将继承列表中 Base和Base1 的位置互换
class Derive :public Base1, public Base
{
public:
                 virtual void FunTest1()
                {
                                cout << "Derive::FunTest1()" << endl;
                }
                 virtual void FunTest3()
                {
                                cout << "Derive::FunTest3()" << endl;
                }
                 virtual void FunTest5()
                {
                                cout << "Derive::FunTest5()" << endl;
                }
                 int _data3;
};

typedef void (*VtbFun)();
void PrintVtable()
{
                cout << "Derive类的虚函数表: " << endl;
                 Derive d1;
                d1._data1 = 0x01;
                d1._data2 = 0x02;
                d1._data3 = 0x03;

                 int* pVTable = (int *)*(int*)&d1;
                 VtbFun FunTest = (VtbFun )*pVTable;
                 while (NULL != FunTest)
                {
                                FunTest();
                                cout << ( int*)FunTest << endl;
                                pVTable += 1;
                                FunTest = ( VtbFun)*pVTable;
                }
                cout << "虚表结束. " << endl;
}
int main()
{
                PrintVtable();
                 return 0;
}

此时派生类的对象模型和虚表的结构:

派生类虚表建立过程:先建立和Base1相同的部分,在Derive中对FunTest3进行了重写,所以替换掉虚表中原来的Base1::FunTest3改为了Derive::FunTest3,为了提高访问速度,将自己特有的虚函数加在第一份虚表的后面,建立Base虚表的过程与Base1相同。

6、虚拟继承

class Base
{
public :
                 virtual void FunTest1()
                {
                                cout << "Base::FunTest1()" << endl;
                }
                 virtual void FunTest2()
                {
                                cout << "Base::FunTest2()" << endl;
                }
                 int _data1;
};
class Derive : virtual public Base
{
public :
                 virtual void FunTest3()
                {
                                cout << "Derive::FunTest3()" << endl;
                }
                 virtual void FunTest4()
                {
                                cout << "Derive::FunTest4()" << endl;
                }
                 int _data2;
};
typedef void (* VtbFun)();
void PrintVtable()
{
                cout << "Derive类的虚函数表: " << endl;
                 Derive d1;
                d1._data1 = 0x01;
                d1._data2 = 0x02;

                 int * pVTable = (int *)*( int*)&d1;
                 VtbFun FunTest = (VtbFun )*pVTable;
                 while (NULL != FunTest)
                {
                                FunTest();
                                cout << ( int *)FunTest << endl;
                                pVTable += 1;
                                FunTest = ( VtbFun )*pVTable;
                }
                cout << "虚表结束: " << endl;
}
int main()
{
                PrintVtable();
                 return 0;
}

时间: 2024-08-25 19:54:03

C++里的继承和多态(下)——单继承、多继承、菱形继承(含虚拟函数的继承)的相关文章

PHP面向对象的三大特征操作——封装、继承、多态(下)

<?php 继承(单继承)特点:一个子类只有一个父类,一个父类可以有多个子类.//父类(基类)class Ren{    public $name;    public function say(){        echo "问候:";    }    }//子类(派生类)可以继承父类的一切class China extends Ren{    public function say(){        parent::say();        echo "最近怎么

Java学习笔记--继承和多态(下)

1.通过继承来开发超类(superclass) 2.使用super 关键词唤起超类的构造方法 3.在超类中覆盖方法 4.区分override和overload 5.在Object类中探索toString()类 6.发现多态性和动态绑定 7.描述解释为什么向下转型是必须的 8.在Object类中探索equals 方法 9.存储,取回,实现ArrayList的对象 10.使用ArrayList类实现Stack 11.超类中使用数据和方法,protected 12.使用final模块来禁止类和方法的覆

Java 继承、多态与类的复用

摘要: 本文结合Java的类的复用对面向对象两大特征继承和多态进行了全面的介绍. 首先,我们介绍了继承的实质和意义,并探讨了继承,组合和代理在类的复用方面的异同.紧接着,我们依据继承引入了多态.介绍了它的实现机制和详细应用.此外,为了更好地理解继承和多态.我们对final关键字进行了全面的介绍. 在此基础上.我们介绍了Java中类的载入及初始化顺序.最后.我们对面向对象设计中三个十分重要的概念–重载.覆盖与隐藏进行了详细的说明. 要点: 继承 组合,继承,代理 多态 final 关键字 类载入及

学习笔记之深入浅出MFC 第8章 C++重要性质----虚拟函数与多态(Polymorphism)

1.虚拟函数的由来 上面我们曾经提过一个例子: CShape shapes[5]; . . . //令5个shapes各为矩形.正方形.椭圆形.圆形.三角形 for ( int i = 0;  i<5;  i++) { shapes[i].display(); } 在上一节中我们说这种一般化的操作无法完成.你还记得为什么吗?是这样的,上面一节中讲到,由于每一个子类图形的绘制不同,所以display()各不相同,所以无法提升到基类中去.那么用基类定义的shapes[]数组,当然也就没有displa

C++里的继承和多态(上)

  继承 1.私有继承:基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问. 公有继承:基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的访问权限,而基类的私有成员在派生类中是不可见的. 在公有继承时,派生类的成员函数可以访问基类中的公有成员和保护成员:派生类的对象仅可以访问基类中的公有成员. 保护继承:基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数访问,不能被它派生类的对象访问. 2.注意: 1>基类的private成员

JavaScript 面向对象程序设计(下)&mdash;&mdash;继承与多态 【转】

JavaScript 面向对象程序设计(下)--继承与多态 前面我们讨论了如何在 JavaScript 语言中实现对私有实例成员.公有实例成员.私有静态成员.公有静态成员和静态类的封装.这次我们来讨论一下面向对象程序设计中的另外两个要素:继承与多态. 1 又是几个基本概念 为什么要说又呢? 在讨论继承时,我们已经列出了一些基本概念了,那些概念是跟封装密切相关的概念,今天我们要讨论的基本概念,主要是跟继承与多态相关的,但是它们跟封装也有一些联系. 1.1 定义和赋值 变量定义是指用 var a;

JAVA基础——面向对象三大特性:封装、继承、多态

JAVA面向对象三大特性详解 一.封装 1.概念: 将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问. 2.好处: 只能通过规定的方法访问数据.     隐藏类的实例细节,方便修改和实现. 3.封装的实现步骤 需要注意:对封装的属性不一定要通过get/set方法,其他方法也可以对封装的属性进行操作.当然最好使用get/set方法,比较标准. A.访问修饰符 从表格可以看出从上到下封装性越来越差. B.this关键字 1.this关键字代表当前

Objective--C三大特性:封装,继承,多态(杂乱的东西)

OC中类的特性共有有三个,它们分别是封装.多态和继承. 1.成员变量的作用域.成员变量的作用域应该也是封装的一种,它对成员变量的作用范围做出了一些限制. @public : 在任何地方都可以直接访问成员变量. @protected:在本类和子类中可以直接访问. @private:只能在本类中访问. @package:在同一个框架内可以访问. 默认情况下在类的声明中定义的成员变量的修饰符是@protected,在类的实现中定义的成员变量的修饰符是@private. 总结:(1)封装是为了保护类中的

Java继承,多态,组合应用

继承:  面向对象的三大特征之一:    是类和类之间的一种拓展关系,是一种从一般到特殊的关系;    格式: sub   extends Super,  我们把sub称为子类或者拓展类, 把super称为父类或者基类(超类)   泛化: 把子类中的共性抽取到父类的过程; 特化: 子类在父类的基础之上,拓展自己特有的状态和特征;    Object: 是所有类的直接父类后间接父类;      class  Student{} 等价于  class  Student extends Object{