C++继承详解一 ——继承、继承关系、赋值兼容规则、作用域

C++是一门面向对象的编程语言(OOP),它有三大特性:封装,继承,多态。
今天我来主要详解一下继承。
一,继承概念:
继承机制(inheritance)是面向对象程序设计中使代码可以复用的最重要手段。他允许程序员在保持原有类特性的基础上进行扩展,增加功能。继承是类之间的关系建模,共享公有的东西,实现各自本质不同的东西。这样产生的类叫做派生类,也叫子类。之前的类叫做基类,也叫父类。
二,继承关系以及继承下的访问限定符
三种类成员访问限定符:public(公有)、private(私有)、protected(保护)。
三种继承关系:public(公有继承)、private(私有继承)、protected(保护继承)。

注:如果继承关系省略,则默认为private继承。

3,继承的定义格式:

单继承:
class <派生类类名>  :<继承方式> <基类类名>
{
    <新定义的类成员>
}

多继承:
class <派生类类名>  :<继承方式> <基类类名>,<继承方式> <基类类名> ...
{
    <新定义的类成员> 
}
class base   //基类(父类)
{
public:
    void fun()
    {}
private :
    int _Bname;
};

//派生类(子类)
class  derive  :public base  //继承关系
{
public:
    void done()
    {}
private:
    int _Dname;
};

四,派生类的构成
派生类的构成有两大部分:自身定义的成员,从基类继承过来的成员。

class base   //基类(父类)
{
public:
    int _Bname;//为了更方便显示结果,我将成员变量定义成共有的。
};

//派生类(子类)
class  derive  :public base  //继承关系
{
public:
    int _Dname;
};

看下图:
这里写图片描述
我们也可以实现一段代码来予以验证

class base   //基类(父类)
{
public:
    int _Bname;//为了更方便显示结果,我将成员变量定义成共有的。
};

//派生类(子类)
class  derive  :public base  //继承关系
{
public:
    int _Dname;
};

int main()
{
    base b1;
    b1._Bname = 1;
    derive d1;
    d1._Dname = 2;
    d1._Bname = 3;
    system("pause");
    return 0;
}

这里写图片描述

从代码中可以看出,对象d1可以对_Bname进行赋值,图片中也很好地展现了出来。这说明成员 _Bname以是d1的构成一部分了。
五,三种继承关系
public继承:
在平时实践的例子中,大多数都属于公有继承。公有继承中,基类的公有成员和保护成员在派生类中维持自己的访问属性,其私有成员为不可见,也就是说派生类不能访问基类的私有成员。私有成员体现了封装性,如果基类的私有成员被派生类访问,则破坏了基类的封装性。
基类的私有成员不能被派生类访问的。如果你想看看基类的私有成员有哪些,你只需在基类中定义一个非私有的成员函数Show()即可。

class base   //基类(父类)
{
public:
    void Show()
    {
        cout << _Bname << endl;
    }
private:
    int _Bname;
};

公有继承是一个接口继承,保持 is-a 原则,每个父类可访问的成员对子类也可访问。因为每个子类对象都是一个父类对象。
private继承:
私有继承中,基类的公有成员和保护成员都会变成私有成员,而私有成员则是不可见的。私有继承和保护继承是一种实现继承,基类的部分成员并未完全成为子类接口的一部分,是 has-a 的关系原则。
protected继承:
保护继承是一种实现继承,基类的部分成员并未完全成为子类接口的一部分,是 has-a 的关系原则。所以如果你不想基类有的成员被派生类访问,但是需要在基类中访问,你就将该成员设为保护成员。这就看出保护成员限定符是因为继承才出现的。

注:使用关键字class时默认的继承方式是private,使用struct是默认的继承方式是public,不过最好标注出来。

六,继承与转换 (在public继承的基础上)
1,子类对象可以赋值给父类对象(切片/切割);
2,父类对象不能赋值给子类对象;
3,子类对象可以赋值给父类的指针/引用,即父类的指针/引用可以指向子类对象。
4,子类的指针/引用不能指向父类对象(可以通过强制类型转换完成)。

上面四点我们来通过图和代码的方式来详解:

这里写图片描述

class base   //基类(父类)
{
public:
    base()
        :_Bname(1)
    {}
    void Show()
    {
        cout << _Bname << endl;
    }
private:
    int _Bname;
};

//派生类(子类)
class  derive  :public base  //继承关系
{
public:
    derive()
        :_Dname(2)
    {}
    int _Dname;
};

void Test()
{
    base b;
    derive d;

    //1,关于赋值

    //b = d;   //子类可以赋值给父类(切片/切割)
    //d = b;     //父类不可赋值给子类。没有找到接收“base”类型的右操作数的运算符

    //2,关于引用/指针

    base* p1 = &d;   //父类指针/引用可以指向子类对象
    base& r1 = d;

    derive* p2 = (derive*)&b;  //可以通过强转实现
    derive& r2 = (derive&)b;

    //如果这样,则会发生什么?
    p2->_Dname = 10;  //发生错误,原因是指针p2强转类型后指向父类,但是父类不存在后四个字节内容,即不存在_Dname;
    r2._Dname = 20;   //错误,原因同上

    b.Show();
    d.Show();
}

int main()
{
    Test();
    system("pause");
    return 0;
}

七,继承体系中的作用域
1,基类和派生类都有自己独立的作用域。
2,子类和父类中有同名成员,子类成员将屏蔽父类对成员的直接访问(在子类成员函数中,可以使用 基类 ::基类成员 访问)。这叫隐藏,后面的文章我会详细讲解。

class base
{
public:
    base(const char* name = "",int id = 0)
        :_name(name),_num(id)
    {}
protected:
    string _name;   //姓名
    int _num;     //身份证号   与子类_num成员同名
};
class derive  :public base
{
public:
    derive(const char* name,int id,int num)
        :base(name,id),_num(num)
    {}

    void Display()
    {
        cout << "身份证号:" << base::_num << endl;  //通过作用域解析符来指定
        cout << "学号:" << _num << endl;
    }
protected:
    int _num;   //学号   与父类_num成员同名
};
void Test()
{
    derive d("paul",110,1);
    d.Display();
}
int main()
{
    Test();
    system("pause");
    return 0;
}
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Pg_dog/article/details/68302737
文章标签: c++ 继承
个人分类: c++
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭