C++继承



                                     C++继承中你不知道的事

一、继承inheritance)是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在原有类特性的基础上进行扩展,增加功能

例:

 

二、继承定义格式:

三、继承关系和访问限定符

3种继承关系:public(公有继承)protected(保护继承)private(私有继承)

在继承的过程中由于继承类型的关系,基类的成员继承类型可能会在子类中发生改变。

子类继承基类:

继承类型为public时:基类中的publicprotectedprivate成员的访问属性均没发生变化。

class Base
{
public:
int _pub;
protected:
int _pro;
private:
int _pri;
};
class D :public Base
{
public:
void test1()
{
_pub = 3;
_pro = 4;
_pri = 7;//不可访问
}
protected:
int data;
};
void test()
{
D d;
d._pub = 2;
d._pro = 9;//不可访问
d._pri = 12;//不可访问
}


继承类型为protected时:基类中的public成员的访问属性降级为protected,其他两个均没发生变化。

class Base
{
public:
int _pub;
protected:
int _pro;
private:
int _pri;
};
class D :protected Base
{
public:
void test1()
{
_pub = 3;
_pro = 4;
_pri = 7;//不可访问
}
protected:
int data;
};
void test()
{
D d;
d._pub = 2;//不可访问
d._pro = 9;//不可访问
d._pri = 12;//不可访问
}


继承类型为private时:基类中的publicprotected成员均降级为子类的private成员,不可见。

class Base
{
public:
int _pub;
protected:
int _pro;
private:
int _pri;
};
class D :private Base
{
public:
void test1()
{
_pub = 3;//不可访问
_pro = 4;//不可访问
_pri = 7;//不可访问
}
protected:
int data;
};
void test()
{
D d;
d._pub = 2;//不可访问
d._pro = 9;//不可访问
d._pri = 12;//不可访问
}
 


例如给一个基类Base,其对象模型如下图所示:

class Base
{public:
Base()
{}
public:
int _public;
protected:
int _protected;
private:
int _private;
};


注:private只可在本类内使用

protected可以在子类和基类内用

public基类、子类和类外均可使用

总结:1、如果基类成员不想在类外被直接访问,但需要在子类中访问,此时可定义为protected。

      2、如果基类成员不想在子类中被访问,可定义为private。

      3、不管哪种继承方式,子类都可以访问基类的公有成员和保护成员,在子类中基类的私有成员存在但不可以访问(不可见)。

      4、使用关键字class时默认继承方式为private,使用关键字struct时默认继承方式为public。(最好显式的写出继承方式)

class D :Base
{
void test1()
{
_pub = 10;
}
public:
int _D_pub;
};
struct E :Base  
{
void test2()
{
  _pub = 20;
}
public:
int _E_pub;
};


子类D继承基类Base,在进入test1()时编译器会报错

 

子类E继承基类Base,在进入test2()后会显示

 

5、在继承关系里面,在派生类中如果没有显式定义六个成员函数,编译系统会默认合成这些函数。(构造函数、拷贝构造函数、析构函数、赋值运算符重载、取址操作符重载、const修饰的取址运算符重载)

6、继承关系中,构造函数的调用顺序:子类对象先调用子类构造函数,在子类构造函数的初始化列表中调用基类的构造函数,完成子类对象的初始化,然后进入子类构造函数的函数体。

析构函数的调用顺序:先调用子类中的析构函数,在调用基类中的析构函数。如下:

class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
 
~Base()
{
cout << "~Base()" << endl;
}
};
class D :public Base
{
public:
D()
{
cout << "D()" << endl;
}
 
~D()
{
cout << "~D()" << endl;
}
};


所以会出现这的结果。

 

说明:1、基类没有缺省的构造函数,子类必须要在初始化列表中显式给出基类名和参数列表。

      2、基类没有定义构造函数,则子类可以不用定义,全部使用缺省构造函数。

      3、基类定义了带有形参表的构造函数,子类就一定要定义构造函数。(隐藏)

四、继承体系中的作用域:

  1. 在继承体系中基类和子类是两个不同的作用域。

  2. 在子类和父类中有同名成员,子类成员将屏蔽对父类成员的直接访问。

    注:实际中继承体系里最好不要定义同名成员。

    隐藏

    class Base
    {
    public:
    void test1()
    {
    cout << "Base::test1()" << endl;
    }
    };
    class D :public Base
    {
    public:
    void test1()
    {
    cout << "D::test1()" << endl;
    }
    };
    Void test()
    {
    D d;
    d.test1();
    //d.Base::test1();
    }


    只调d,test1(),打印如下:

     

    此处隐藏了Base中的test1()

    d.Base::test1(),打印如下:

     

    五、隐藏与重载的区别:

    重载:必须在同一作用域内,函数名相同,参数不同。

    隐藏:在不同的作用域内,函数名相同,参数可以不相同。

    六、继承--赋值兼容规则

  1. 子类对象可以赋值给基类对象

  2. 父类对象不能赋值给子类对象

  3. 父类的指针/引用可以指向子类对象

  4. 子类的指针/引用不能只想父类对象

    class Base
    {
    public:
    int _pub;
    };
    class D :public Base
    {
    public:
    int _pub;
    };
    void test()
    {
    Base b;
    D d;
    b = d;//子类对象可以给父类对象赋值
    //d = b;//父类对象不可以给子类对象赋值
    Base *pb = &b;//创建基类指针
    pb = &d;//父类的指针或引用可以指向子类对象
    D *pd = &d;//创建子类指针
    pd = &b;//子类的指针或引用不可以指向父类
    pd = (D*)&b;
    pb->_pub = 2;//可以强制类型转换,但有可能造成程序崩溃
    }


    说明:

    基类指针  Base *pb;

    该指针即可指向基类对象,又可以指向派生类对象,但指向派生类的对象是派生类中集成的基类的对象。

     

     

    七、类的组合:在一个类中以另一个类的对象作为数据成员的称为类的组合。

    class Base
    {
    public:
    int _pub;
    };
    class D :public Base
    {
    public:
    void test1()
    {
    _b._pub = 0;
    }
    private:
    Base _b;
    };


     

    八、友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员。

    class Base
    {
    friend void  test1(Base &b, Derive  &d);
    public:
    int _data;
    };
    class Derive :public Base
    {
    public:
    int data;
    };
    void test1(Base &b,Derive &d)
    {
    cout << b._data << endl;
    cout << d._data << endl;//不可访问,友元关系不能被继承
    cout << d.data << endl;
    }


    编译器会报错,故表明友元关系不能继承。

     

     

    九、继承与静态成员

    基类里面定义static成员,则整个继承体系中只有一个这样的成员。无论派生多少个子类,都只有一个static成员实例。

    class Base
    {
    public:
    Base()
    {
    data++;
    }
    public:
    static int data;
    protected:
    int _pro;
    };
    int Base::data = 3;
    class D :public Base
    {
    protected:
    int _D_pro;
    };
    void test()
    {
    D d1;
    D d2;
    D d3;
    cout << Base::data << endl;
    D::data = 3;
    cout << Base::data << endl;
    }


     结果为如下,静态成员可以继承。

     

     

     

     

    十、单继承、多继承和菱形继承

     

     

      1、单继承:即一个子类只有一个父类。

     单继承对象模型

     

    class B
    {
    protected:
    int _data;
    };
    class C :public B
    {
    protected:
    int _data1;
    };
    class D :public C
    {
    protected:
    int _data2;
    };
     


  1. 多继承:即一个子类有两个或两个以上直接父类。

    多继承对象模型

     

      

     class B
    {
    protected:
    int _data;
    };
    class C
    {
    protected:
    int _data1;
    };
    class D :public B, public C
    {
    public:
    int _data2;
    };

  2. 菱形(钻石)继承

    菱形(钻石)继承对象模型

     

    class B
    {
    protected:
    int data1;
    };
    class C1 :public B
    {
    protected:
    int data2;
    };
    class C2 :public B
    {
    protected:
    int data3;
    };
    class D :public C1, public C2
    {
    protected:
    int data4;
    };


    菱形继承存在二义性和数据冗余问题。为了解决此问题引出了虚继承的概念。

  1. 虚继承

    class  B
    {
    public:
    int data;
    };
    class C1 :virtual public B//虚继承
    {
    public:
    int data1;
    };
    class C2 :virtual public B//虚继承
    {
    public:
    int data3;
    };
    class D :public C1, public C2
    {
    public:
    int data4;
    };
    void test()
    {
    D d;
    d.data = 2;
    d.data1 = 4;
    d.data3 = 7;
    d.data4 = 8;
    }


     

     

     

     

    调内存块,可见其在内存中的布局如下:

     

     

    虚继承的对象模型:

  2.  以上代码在VS2013环境下运行

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值