请选择 进入手机版 | 继续访问电脑版

小晴分享:让你彻底明白C++之继承与派生

发表于 2016-09-13 13:52 显示全部楼层 38 1963

本帖最后由 雪后初晴 于 2016-9-17 15:20 编辑

Hello,大家好。

最近在论坛水了一堆的帖子,并且好久没有分享技术贴了。

我觉得,作为版版不能一直任性地水下去。

所以今天,想给大家带来一点真实有用的东西。

那便是:让你彻底明白C++之继承与派生。

 

继承的地位:

众所周知,面向对象的程序设计是封装、继承与多态。

其实“封装”只是基于对象的特性,“继承与多态”才是面向

对象真正的精髓。继承是面向对象的代表特性,又作为

多态的前奏课程,于是显得格外重要。

 

小晴分享の目录(按照咱这个顺序,你将轻松搞定继承派生哦

一.没有继承,究竟会发生什么?

二.继承的结构

三.继承的权限控制

四.继承的构造函数初始化。

五.多级派生下的各种问题。


一.没有继承,究竟会发生什么?——论继承的必要性,重要性

现在我们需要完成一个需求,这个需求内容是:

1.创建一个人类

2.创建一个学生类

3.创建一个老师类


#pragmar once
#include<iostream>
#include<string>

using namespace std;

enum Sex{M,F};

class Person

{


public:
    explicit Person(string name=0,int age=0,Sex sex=static_cast<Sex>(0))
        :name(name),age(age),sex(sex)
    {    }


private:
    string name;    //姓名
    int age;        //年龄
    Sex sex;        //性别


};



class Student
{
public:
    Student(string name=0,int age=0,Sex sex=static_cast<Sex>(0),
    
                    double cppScore,double javaScore)

        :name(name),age(age),sex(sex),cppScore(cppScore),javaScore(javaScore)

    {    }

private:
    string name;        //姓名
    int age;            //年龄
    Sex sex;            //性别
    double cppScore    //Cplusplus成绩
    double javaScore    //Java成绩
};




class Teacher

{
public:
    Teacher (string name=0,int age=0,Sex sex=static_cast<Sex>(0),
                    double cppScore,double javaScore)
       :name(name),age(age),sex(sex),cppScore(cppScore),javaScore(javaScore)
   {    }



private:

    string name;        //姓名
    int age;            //年龄
    Sex sex;            //性别
    double salary       //薪水
};


以上代码有什么问题呢?
我们发现一个共性:
人类具有姓名、年龄、性别;
学生类具有姓名、年龄、性别;
教师类具有姓名、年龄、性别...

推而广之...

演员类具有姓名、年龄、性别;
公务员类具有姓名、年龄、性别;
医疗人员类具有姓名、年龄、性别。

... ...

现在已经实现了人类和学生类,要求你在此基础上,
把演员类、公务员类、医疗人员类、XXX类,YYY类,ZZZ类...
(XXX,YYY,ZZZ从属于人类)全部实现。

请问,你忍受得了写那么多重复的代码吗?
是我,我就一怒之下拔掉电源,不写代码了。


”光明源于黑暗,黑暗涌现光明。“——”继承“来拯救你。

#pragmar once
#include<iostream>
#include<string>


using namespace std;
class Person
{
public:
    explicit Person(string name=0,int age=0,Sex sex=static_cast<Sex>(0))
        :name(name),age(age),sex(sex)
    {    }

private:
    string name;    //姓名
    int age;        //年龄
    Sex sex;        //性别
};



class Student : public Person
{
public:
    Student(string name=0,int age=0,Sex sex=static_cast<Sex>(0),

            double cppScore,double javaScore)

        :Person(name,age,sex),cppScore(cppScore),javaScore(javaScore)

    {    }


private:
    double cppScore    //Cplusplus成绩
    double javaScore    //Java成绩

};




class Teacher : public Person
{
public:
    Teacher (string name=0,int age=0,Sex sex=static_cast<Sex>(0),
                    double cppScore,double javaScore)
        :Person(name,age,sex),cppScore(cppScore),javaScore(javaScore)
    {    }



private:
    double salary       //薪水    
};

相比以上代码,我们发现:

表面现象:

在Student类和Teacher类中没有出现姓名、年龄、性别这3个属性。

可以少些好多代码。

 

隐藏却真实存在的情况:

当实例化类对象时,会少分配些内存空间。节约空间资源。

 


代码抽象,我们不妨从生活中解读继承:


1.你是你爸妈的儿女,所以能够继承他们的财产、房屋等,

这可以让你少奋斗几年甚至几十年;


2.你到外地租房,所以能够继承上一房客留下的部分东东,

比如棉被、枕头、家用拖把、扫帚等。

这可以节省你至少100元以上的购物资金。


3.古时去门派学艺,所以能够继承门派高人留下的独门绝技。

比如九阴真经,九阳神功,六脉神剑...

这不仅可以让你初入江湖的你少受人欺负,更可助常年血战

血出的你笑傲江湖。


... ...


因此。继承是很有必要的。

没有继承,客观上会费很大人力、物力、财力,

主观上你要多奋斗好几年甚至几十年。

别人都功成名就了,你还在苦苦奋斗。你忍受得了吗?



二.继承的结构。

    具体如下:

    1.继承来自基类所有的属性和方法。

    2.调整继承内容的访问权限。

    3.派生自己的个性特征。


    解释如下:

    1.对于1,顾名思义即继承的含义。

    但我们需要注意的是:继承必须是继承所有,不可

    ”取其精华,去其糟粕。“

    

    这点与现实不同,比如你可能想继承你老爸的财产、

    老爸的房子、车子,但不想继承老爸的暴躁脾气。

    

    在程序领域,继承必须全盘吸收,不可任性使脾气。

    有选择性地继承想要的,丢弃自己不想要的。

    

    2.对于2,把它作为一个大内容专门来谈。

 

    3.如果你是你父亲的儿女,却没有自己的个性特征。那么

    你就是他,他就是你。你又有什么存在的价值和意义呢?

    你就是他的复制品,这样的人生多悲哀呀...

 

    派生个性,就是我们派生类相对于基类的存在意义。

 

三.继承的权限——既然继承,为什么还设置了权限?不让你所有?!

这点如果仅从代码上看,不太好理解。我们不妨从生活的角度观之。

 

生活情景一:

1.你在继承你老爸的财产、房子、屋子时,你老爸一定千叮万嘱让你

  小心又小心。告诉你财产该怎么保存,不能给你儿女乱用。转交给

 你房产证的同时,告诉你不要将父辈的房产证让你妻子所有。转交

 给你车子的同时,告诉你车子哪里破旧了,甚至哪里坏了,注意事

 项是什么,以便让你安全驾驶,顺利出行。

 

生活情景二:

2.现在让我们转换了时空、身份和姓名,来到古代的武林。情况如下:

我拜在了明教教主张无忌的门下,你拜在了少林派掌门智空大师的门下。

  学武之初,我们必学门派根基武功,运气,身法、基本剑术。

  一段时日后,我们会学习到一定的招式技巧,助你打翻虾兵小将,

甚至于一些部门统领,但想对付武林高手还望尘莫及。

  然后,大多数人就到此为止了。

  只有少部分天资聪慧的人,才可以接触到门派的独门绝技,其威力

十分巨大。可以与武林高手一较高下,但时赢时输。

  只有胸怀天下,心系苍生,人品绝佳又天资聪慧的人,才是张无忌

和智空大师的真正继承人。可以练就”乾坤大挪移“,”九阳神功“,”大力

金刚指“,”金刚伏魔圈“等惊世绝学。决胜千里之外,运筹帷幄之中。


生活情景一说明:

  你虽然继承了老爸的财产,房子,车子,但不可任凭你的意愿使用。

他叮嘱提醒你切记不可怎样怎样,可以将之看成权限的赋予。

  

  权限具体化如下:

 ①你能拥有我的财产,但不能用钱来纵容儿女任性;

 ②你能拥有我的房子,但不能将房产证转交给妻子。以免未来闹分

        手的时候失去太多。

 ③你能拥有我的车子,但是车子我已使用多年,很多零部件陈旧了,

        你开车的时候需要注意xxx。


生活情景二说明:

  你虽然加入了XXX门派,但不是你想学什么就能学什么。而且,你

也不应该什么都学会。只有满足一定条件,才赋予你学习的权限。

  

  权限具体化如下:

    ①.凡入我门派者,所有根基武功必定教会,为后续的学习打下基础。

    ②.凡能坚持学习武功之人,给予一定的招式技巧指导,要求必须维护

        本教荣誉,遵守本教教条,谨记惩奸除恶,务必疾恶如仇。

    ③.凡满足以上条件,并且天资聪慧的人,传授独门绝技。这些人是

        本门派的中流砥柱,不可或缺。可以晋升XX部门总管。

    ④.凡满足以上条件,人品绝佳且天资聪慧的人,传授毕生绝学。该

        弟子就是本门派的未来的掌门人/副掌门。

    

    好了,说了蛮多。让我们回到代码的世界中去。

#pragram once 
#include <iostream>

using namespace std;


class Person
{
public:
    //构造函数
    Person(string name=0,const char sex=static_cast<Sex>(0),
            int age=0,double salary=0)
          :name(name),sex(sex),age(age),salary(salary)
    {    }
    string name;              //姓名
    const char sex;           //性别
protected:
    int age;                  //年龄(年龄一般是不希望不让别人随意知道的,尤其是女人。)
private: 
    double salary;            //薪水   
};

class Student::public Person
{
public:
    //构造函数
    Student(string name=0,const char sex=static_cast<Sex>(0),
            int age=0,double cppScore=0,double javaScore)
          :Person(name,sex,age),cppScore(cppScore),javaScore(javaScore)
    {    }
protected:
    double cppScore;            //CPlusplus成绩
    double javaScore            //Java成绩

};

class Student::protected Person
{
    //同上
};

class Student::private Person
{
    //同上
};

/* 
  细心的你会发现,代码除了::public/protected/private不同外,其余全部相同。
  那么究竟有什么区别呢?
*/

这里先给出一张权限表,供大家参考:

    类的成员权限:


public成员
protected成员
private成员
本类



继承链上的类


×
全局和其他类

×
×

 

    继承与派生的权限:


public派生
protected派生
private派生
原基类public成员
public
protected不可见
原基类proteced成员
protectedprotected
不可见
原基类private成员
不可见
不可见
不可见

 

综合以上两表即是继承与派生的访问权限。

 

访问成员遵循以下步骤:

1.先由继承和派生,调整基类与派生类的访问权限。

2.然后各类成员权限更改完毕后,遵循本类成员的访问权限。

3.private成员也是继承过来的内容,只是不可见,无法访问。

(看不见,摸不着,无法访问的事物,不代表他不存在。

    亲一定要明白这个道理哦)

//以Protected继承为例,Student类内容可形象化如下:
//注:子类里并不要写继承下来的内容,否则会覆盖。
//这里是为了演示。

class Student
{
protected:
    string name;    //name原来为public,现在为protected
    Sex sex;        //sex原来为public,现在为protected
protected:
    int age;        //sex原来为protected,现在为protected

不可见:
    double salary;    //salary原来为private,现在为不可见
};

 

四.继承的构造函数初始化

    继承与派生,是因为先继承了才派生。所以:

    ①在调用派生类构造函数前,必须先调用基类的构造函数。

    ②如果双类中有子对象,那么本类中根据声明顺序进行初始化。


    综合①②可得,

    1.对基类按成员变量声明顺序进行初始化。

            ↓

    2.对派生类按成员变量声明顺序进行初始化。


    注意:

    对于本类初始化,初始化列表顺序无所谓。

    对于继承关系的类组初始化,初始化列表顺序必须遵循先基类后子类的原则。

    无论是本类还是类组,构造函数的参数列表的顺序可任意。


    以上内容从生活角度解释如下:


    1.首先要明确构造函数的功能,构造函数是用来创建类对象的。基类的构造

    函数创建了你的父亲,派生类的构造函数创建了你。显然,这个世界上先有

    你的父亲,才有了你。你是在你爸出生以后出生的。如果不先初始化基类,

    而直接初始化派生类,那么将是你先出生,然后你父亲出生。岂不荒谬?


    2.只有你爸出生了,你才能出生,进而才能继承他的东东。他都没出生,你

    怎么能出生?更别提继承他的东东了。


五.多级派生下的各种问题。

    多级派生,见证了累积的伟大。

    这让我想到了中学时代学过的课文《愚公移山》里的一段著名语录:

    

    汝心之固,固不可彻,曾不若孀妻弱子。虽我之死,有子存焉;子又生孙,

    孙又生子;子又有子,子又有孙;子子孙孙无穷匮也,而山不加增,何苦

    而不平?

 

    形象化理解多级派生,就是父母生了你,你与她又会生出A,A与A的老婆

    生出B,B与B的老婆生出C,... ...

 

    下面我们回到代码:

class MyClass0 
{};

class MyClass1 : public MyClass0
{};

class MyClass2 : public MyClass1
{};

... ... 

class MyClass(i) : public MyClass(i-1)
{};

... ... 

class MyClass(n) : public MyClass(n-1)
{};

其中:(0<i<=n,n为正整数)

 

可以看到,多级派生,就是下一层继承上一层,层层递进。

多级派生结构就是单级派生+单级派生+... +单级派生。

 

因此,单级派生的特点,多级派生也有。但需要额外考虑以下内容:

1.层级之间继承的权限赋予问题

2.层级之间构造函数初始化问题

 

先看第1点,下面回到代码中去:

#pragma once

#include <iostream>

using namespace std;

enum Sex{M,F};
class Person
{
public:
    string name;
    int age;
    const Sex sex;
protected:
    string address;
private:
    float salary;
}

class Student : public Person
{
public:
    float javaScore;
protected:
    float cppScore;

}

class Monitor : protected Student
{
protected:
    void rollcall();    //点名
}

通过继承,类中成员变量的访问属性如下:


name
age
sex
address
salary
javaScore
cppScore

Person

(基类)

publicpublic
public
protected
private
×
×

Student

(public继承)

public
public
public
protected
不可见
public
protected

Monitor

(protected继承)

protected
protected
protected
private
不可见
protected
protected

 

总结一句话就是:上级类成员的访问权限,影响下一级继承后类成员的访问权限。

 

下面看看第二点,

#pragma once
#include <iostream>
using namespace std;
enum Sex{M,F};
class Person
{
public:
    Person(string name=0, int age=0, const Sex sex=static_cast<Sex>(0))
        :name(name),age(age),sex(sex)
    {    }
    string name;
    int age;
    const Sex sex;

};

class Student : public Person
{   
public:
    Student(string name=0, int age=0, const Sex sex=static_cast<Sex>(0),
            float javaScore=0,float cppScore=0,string address=0,float salary=0)
        :Person(name,age,sex),
        javaScore(javaScore),cppScore(cppScore),address(address),salary(salary)
    {    }   

protected:
    float javaScore;
    float cppScore;
    string address;
    float salary;
};

class Monitor : protected Student
{
public:
    Monitor(string name=0, int age=0, const Sex sex=static_cast<Sex>(0),
            float javaScore=0,float cppScore=0,string address=0,float salary=0,
            string level)
        :Student(name,age,sex,javaScore,address,float salary).level(level)        
    {    }   
protected:
    string level;
    void rollcall();    //点名
};

 

可以很清晰地看到,下一级派生类只需要调用上一级基类的构造函数。

而不是:A←B←C,C先调用A的构造,然后调用B的构造,最后调用自己的构造。



至此,单重继承的主要内容已全部分享完毕。

对于多重继承,这实在是一个糟糕的技术。

不仅非常难用,而且容易犯错。

在更现代的语言,例如Java和C#中已经将它废弃掉。

所以这儿就不打算更新了。

如果有一天,我有足够的闲情雅致,

我很愿意将对它的所思所感,所见所闻分享给大家。


最后,希望各位能喜欢,并多多给予支持。谢谢。

 

 

回复 使用道具
举报
begeekmyfriend

发表于 2017-02-10 00:46 显示全部楼层

回复 支持 反对 使用道具
举报
南瑟1

发表于 2017-02-09 15:56 显示全部楼层

回复 支持 反对 使用道具
举报
我爱大菠萝

发表于 2017-02-09 14:33 显示全部楼层

为了学币每天必水一下,望见谅

回复 支持 反对 使用道具
举报
哥哥在哪儿

发表于 2017-02-09 13:44 显示全部楼层

回复 支持 反对 使用道具
举报
甜甜车

发表于 2017-02-09 12:40 显示全部楼层

回复 支持 反对 使用道具
举报
腐女大作战

发表于 2017-02-09 06:35 显示全部楼层

回复 支持 反对 使用道具
举报
年轻的音符

发表于 2017-02-09 04:46 显示全部楼层

回复 支持 反对 使用道具
举报
p出我世界

发表于 2017-02-09 01:44 显示全部楼层

回复 支持 反对 使用道具
举报
begeekmyfriend

发表于 2017-02-08 15:42 显示全部楼层

回复 支持 反对 使用道具
举报
1234下一页

发表新文章

5

学分

2273

学币

3318

积分

版主

Rank: 7Rank: 7Rank: 7

积分
3318

小码哥版主勋章前100注册用户勋章小码哥一周年勋章活动达人勋章真土豪勋章勋章意见领袖勋章论坛百帖达成勋章

Ta的主页 发消息
精华帖排行榜

精彩推荐

  • 关注小码哥教育