继承&多态

· 访问限定符

*继承访问控制的作用是将继承下来的成员在派生类域内的属性改变而和原本基类的成员访问控制符关。

 所有的基类private成员在派生类里是不可见,但它确确实实被继承下来了。

 基类的protected成员只对继承它的派生类可见,在类外不可使用


· 派生类列表

  在定义派生类的时候我们必须写出派生类列表,但在声明处不可写。


1.虚函数的使用(多态的实现)

#include<iostream>
using namespace std;
class Base
{
private:
     int _base ;
public:
     Base()
            :_base (0)
     {}
     virtual void show()
     {
            cout << "Base::show()" << endl ;
     }
     //....
};
class D :public Base
{
public:
     D()
            :_d (1)
     {}
     void show()
     {
            cout << "D::show()" << endl ;
     }

private:
     int _d ;
};
void Print (Base *pb)
{
     pb-> show();
}
int main ()
{
     Base b;
     D d;
     Print(& b);
     Print(& d);
     getchar();
     return 0;
}


wKioL1btOM-x6FbyAAAJ7xNtKBI508.png

*通过传参的不同使得show函数的输出结果不同,实现的原理是:虚函数表(动态绑定)

*动态绑定:函数的运行版本由实参决定,在函数运行时才进行选择,又称运行时绑定

*规则:基类通常都需要定义一个虚析构函数,无论是否在后面用到

*成员函数如果没有被声明为虚函数并且不是静态函数,那它的解析过程发生在编译阶段而不是运行阶段

*每个类控制自己的成员初始化,不要使用派生类直接初始化基类成员

*派生类必须对它内部重新定义的虚函数进行声明

*任何除了构造函数以外的非静态函数都可以是虚函数


2.虚函数表

 a.单继承

  如上例中代码示,单继承时Base的对象模型如下

wKioL1btOPewHXhWAAAWc8Njv0M657.png

 D的对象模型如下

wKiom1btOJXz7HUgAAAWf_gvT9M244.png

 b.多继承 

#include<iostream>
using namespace std;
typedef void (*PTR)();
class Base
{
private:
     int _base ;
public:
     Base()
            :_base (0)
     {}
     virtual void show()
     {
            cout << "Base::show()" << endl ;
     }
     //...
};
class Base2
{
private:
     int _base2 ;
public:
     Base2()
            :_base2 (2)
     {}
     virtual void show()
     {
            cout << "Base2::show()" << endl ;
     }
     virtual void fun2()
     {
            cout << "Base2::fun2()" << endl ;
     }
     //...
};
class D :public Base,public Base2
{
public:
     D()
            :_d (1)
     {}
     void show()
     {
            cout << "D::show()" << endl ;
     }
     void fun2()
     {
            cout << "D::fun2()" << endl ;
     }
     virtual void fun3()
     {
            cout << "D::fun3()" << endl ;
     }
     virtual void fun4()
     {
            cout << "D::fun4()" << endl ;
     }
private:
     int _d ;
};
void PrintVT (int p )
     //实参是每个类存储虚函数指针的地址,解引用之后代表该类的虚函数指针
{
     PTR ptr = NULL;
     //现在让pi能访问类的虚函数指针
     int *pi = (int *)p;
     int i = 0;
     while (pi [i ])
     {
            ptr = (PTR)pi[i];
            ptr();
            i++;
     }
}
int main ()
{
     //Base b;
     //Base2 b2;
     D d;
     //PrintVT(*(int *)&b);
     //cout << "--------------------" << endl;
     //PrintVT(*(int *)&b2);
     //cout << "--------------------" << endl;
     PrintVT(*( int *)&d );
     cout << "--------------------" << endl ;
     getchar();
     return 0;
 }


 如上述代码,我们发现多继承的虚函数表和单继承由很大的不同之处

 在此不再赘述两个基类的虚表,我们来看派生类的对象模型

wKiom1btOGeSUmyqAAAfqhZS5ZI278.png

 spacer.gif

*我们发现在派生类中有两个虚函数表,并且派生类自己的虚函数被存入了第一个虚函数表

上述代码运行的结果是

wKioL1btOYqgjYTEAAAXcyFD6Kg565.png


tips:

   *在一个对象中继承自基类的部分和派生类的自定义部分不一定是连续存储的

   *如果在基类里定义了一个静态成员那么这个成员是唯一定义的,只存在一个实例

   *含有关键字final的类不可以作为基类被继承

   *动态绑定基于给用基类的指针或者引用绑定对象。除了内置指针外智能指针也是可以做到这样的     类     型转换的

          

C++的多态性:我们把具有继承关系的多个类型称为多态类型,因为我们可以使用这些类型的多种形式而无须在意他们的差异


覆盖:派生类重新定义了基类的虚函数并且他们的函数名,参数列表和返回值都必须相同但是协变的返              回值必须不同。

隐藏:派生类重新中定义了基类中同名的函数(非虚函数和静态函数)此时基类中的同名函数被隐藏

重载:在同一个作用域内我们定义了几个同名函数他们通过参数列表来区分彼此称为重载


*我们可以在支持C++11的编译器中给覆盖加上override关键字,那么我们在编写该函数时编译器会检查   它形式的正确性,保证我们的调试变得更加简单