c++ 继承中的覆盖,隐藏,虚拟继承

首先,声明一点,覆盖和隐藏是基于继承来讨论的;至于重载貌似和继承没有太大联系,所谓重载就是同一函数名,不同的参数列表(类型或个数),这里不做讨论。

1 覆盖

特点:(1)父类的成员函数是虚拟函数,即在成员函数前加上virtual关键字

              (2)子类的同名成员函数和父类的完全相同,即参数和返回值类型完全一样

满足上述2条特征即为覆盖

目的: 其中一个重要的作用就是通过父类访问子类

实例分析

#include<iostream.h>
class Base
{
public:
 Base()
 {
 }
    virtual void test()
 {
  cout<<"Test base."<<endl;
 }
private:
};
class Child1 : public Base
{
public:
 Child1()
 {
 }
 void test()
 {
  cout<<"Test Child1."<<endl;
 }

private:
};
class Child2 : public Base
{
public:
 Child2()
 {
 }
 void test()
 {
  cout<<"Test Child2."<<endl;
 }

private:
};
int main()
{
 Base a;
 Child1 b;
 Child2 c;
 Base* d[3] = {&a, &b, &c}; 
 for(int i = 0; i < 3; i ++)
 {
  d[i]->test();
 }
 return 0;
}

结果如下:
 

分析:Base* d[3] = {&a, &b, &c}这句代码将b,c二种子类转化为父类,由于覆盖原则,父类中的test函数将被b,c子类的函数覆盖,因此通过d[i]->test调用的是子类的构造函数,从而实现了通过父类访问子类。

倘若没有覆盖,又是怎样的结果?(本例是将Base类中的成员函数的virtual关键字去掉),代码如下:

#include<iostream.h>
class Base
{
public:
 Base()
 {
 }
    void test()
 {
  cout<<"Test base."<<endl;
 }
private:
};
class Child1 : public Base
{
public:
 Child1()
 {
 }
 void test()
 {
  cout<<"Test Child1."<<endl;
 }

private:
};
class Child2 : public Base
{
public:
 Child2()
 {
 }
 void test()
 {
  cout<<"Test Child2."<<endl;
 }

private:
};
int main()
{
 Base a;
 Child1 b;
 Child2 c;
 Base* d[3] = {&a, &b, &c};
 for(int i = 0; i < 3; i ++)
 {
  d[i]->test();
 }
 return 0;
}

结果如下:
 

通过比较2者差别,应该了解到覆盖的作用

2. 隐藏

隐藏是子类中的同名函数将父类中隐藏

特点:(1)父类和子类有同名函数

           (2)同名函数不一样(参数不一样或者返回类型不一样)

实例分析:

#include<iostream.h>
class Base
{
public:
 Base()
 {
 }
    void test(int i)
 {
  cout<<"Test base."<<endl;
 }
private:
};
class Child1 : public Base
{
public:
 Child1()
 {
 }
 void test()
 {
  cout<<"Test Child1."<<endl;
 }

private:
};
int main()
{
 Child1 a;
    a.test();
 //a.test(1); //错误,因为子类将父类同名函数隐藏
 a.Base::test(1);//可以采取这种方式访问父类的同名函数
 return 0;
}

运行结果:

总结:由于隐藏,可能导致子类不能正常的访问父类的成员函数,我们可以采取a.Base::test(1)类似的方式解决

3 虚拟继承

这次我们换一种策略讲解

先看实例:

#include<iostream>
using namespace std;
class base
{
public:
 base()
 {
  cout<<"I am in base!"<<endl;
 }
 vprintkk()
 {
  cout<<"Test"<<endl;
 }
private:

};
class a: public base
{
public:
 a()
 {
  cout<<"I am in a!"<<endl;
 }
private: 
};
class b: public base
{
public:
 b()
 {
  cout<<"I am in b!"<<endl;
 }
private: 
};
class c : public a, public b
{
public:
 c()
 {
  cout<<"I am in c!"<<endl;
 }
private:
};
int main()
{
 c test;
 
 //test.vprintkk();  //会产生2义性问题
 test.a::vprintkk();//正确,通过a访问
 test.b::vprintkk();//正确,通过b访问
 return 0;
}

分析:父类是Base,a,b继承于Base,c继承与a,b;当定义一个 c类型的test对象时,想通过test对象访问基类的vprintkk成员函数时,由于test从a,b二个类都继承了vprintkk函数,这时候编译器就不知道是通过a,还是通过b访问vprintkk,会产生二义性问题。

这时候虚拟继承就出现了

虚拟继承:C++使用虚拟继承(Virtual Inheritance),解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。

改过后的代码如下:

#include<iostream>
using namespace std;
class base
{
public:
 base()
 {
  cout<<"I am in base!"<<endl;
 }
 vprintkk()
 {
  cout<<"Test"<<endl;
 }
private:

};
class a: virtual public base
{
public:
 a()
 {
  cout<<"I am in a!"<<endl;
 }
private: 
};
class b: virtual public base
{
public:
 b()
 {
  cout<<"I am in b!"<<endl;
 }
private: 
};
class c : public a, public b
{
public:
 c()
 {
  cout<<"I am in c!"<<endl;
 }
private:
};
int main()
{
 c test;
 
 test.vprintkk();  //采用了虚拟继承,不会产生2义性问题
 test.a::vprintkk();
 test.b::vprintkk();
 return 0;
}

运行结果

猜你喜欢

转载自blog.csdn.net/xiaocong1314/article/details/8807793
今日推荐