C++继承和派生

文章目录

  • C++继承和派生
    • 一、继承和派生的概念
      • 1.继承的概念
      • 2.派生类
      • 3.继承实例程序
    • 二、类之间的两种关系
      • 1.继承关系
      • 2.复合关系
    • 三、派生类覆盖基类成员&类的保护成员
      • 1.派生类覆盖基类成员
      • 2.类的保护成员
    • 四、派生类的构造函数
      • 1.示例说明
      • 2.包含成员对象的派生类的构造函数写法
      • 3.封闭派生类对象的构造函数执行顺序
      • 4.封闭派生类对象消亡时析构函数的执行顺序
    • 五、public继承的赋值兼容规则
      • 1.public继承的赋值兼容规则
      • 2.protected继承和private继承
      • 3.基类与派生类的指针强制转换
    • 六、直接基类和间接基类
      • 1.直接基类和间接基类概念
      • 2.直接基类和间接基类示例程序

一、继承和派生的概念

1.继承的概念

继承:在定义一个新的类B时,如果该类与某个已有的类A相似(指的是B拥有A的全部特点),那么就可以把A作为一个基类,而把B作为基类的一个派生类(也称子类)。

  • 派生类是通过对基类进行修改和扩充得到的。在派生类中,可以扩充新的成员变量和成员函数。
  • 派生类一经定义后,可以独立使用,不依赖于基类。
  • 派生类拥有基类的全部成员函数和成员变量,不论是private、protected、public 。
  • 在派生类的各个成员函数中,不能访问基类中的private成员。

2.派生类

  • 派生类的写法

    class 派生类名:public 基类名
    {

    };

    class CStudent
    {private:string sName;int nAge;public:bool IsThreeGood(){};void SetName(const string &name){sName = name;}//......
    };
    class CUndergraduateStudent : public CStudent
    {private:int nDepartment;public:bool IsThreeGood(){......}; //覆盖bool CanBaoYan(){....};
    }; // 派生类的写法是:类名: public 基类名
    class CGraduatedStudent : public CStudent
    {private:int nDepartment;char szMentorName[20];public:int CountSalary(){...};
    };
    
  • 派生类对象的内存空间

    派生类对象的体积,等于基类对象的体积,再加上派生类对象自己的成员变量的体积。在派生类对象中,包含着基类对象,而且基类对象的存储位置位于派生类对象新增的成员变量之前。

    class CBase
    {int v1, v2;
    };
    class CDerived : public CBase
    {int v3;
    };
    

3.继承实例程序

  • 代码:

    #include <iostream>
    #include <string>
    using namespace std;
    class CStudent
    {private:string name;string id;   //学号char gender; //性别,'F'代表女,'M'代表男int age;public:void PrintInfo();void SetInfo(const string &name_, const string &id_,int age_, char gender_);string GetName() { return name; }
    };
    class CUndergraduateStudent : public CStudent
    { //本科生类,继承了CStudent类
    private:string department; //学生所属的系的名称
    public:void QualifiedForBaoyan(){ //给予保研资格cout << "qualified for baoyan" << endl;}void PrintInfo(){CStudent::PrintInfo(); //调用基类的PrintInfocout << "Department:" << department << endl;}void SetInfo(const string &name_, const string &id_,int age_, char gender_, const string &department_){CStudent::SetInfo(name_, id_, age_, gender_); //调用基类的SetInfodepartment = department_;}
    };
    void CStudent::PrintInfo()
    {cout << "Name:" << name << endl;cout << "ID:" << id << endl;cout << "Age:" << age << endl;cout << "Gender:" << gender << endl;
    }
    void CStudent::SetInfo(const string &name_, const string &id_,int age_, char gender_)
    {name = name_;id = id_;age = age_;gender = gender_;
    }
    int main()
    {CUndergraduateStudent s2;s2.SetInfo("Harry Potter ", "118829212", 19, 'M', "Computer Science");cout << s2.GetName() << " ";s2.QualifiedForBaoyan();s2.PrintInfo();return 0;
    }
    
  • 输出结果:

    Harry Potter qualified for baoyan
    Name:Harry Potter
    ID:118829212
    Age:19
    Gender:M
    Department:Computer Science

二、类之间的两种关系

1.继承关系

  • 继承:“是”关系。

    • 基类 A,B是基类A的派生类。
    • 逻辑上要求:“一个B对象也是一个A对象”。
  • 继承关系的使用

    • 写了一个 CMan 类代表男人

      • 后来又发现需要一个CWoman类来代表女人
      • CWoman类和CMan类有共同之处
      • 就让CWoman类从CMan类派生而来,是否合适?

    是不合理的! 因为“一个女人也是一个男人”
    从逻辑上不成立好的做法是概括男人和女人共同特点,写一个 CHuman类,代表“人”,然后CMan和CWoman都从CHuman派生。

2.复合关系

  • 复合:“有”关系。

    • 类C中“有”成员变量k,k是类D的对象,则C和D是复合关系
    • 一般逻辑上要求:“D对象是C对象的固有属性或组成部分”。
  • 复合关系的使用

    几何形体程序中,需要写“点”类,也需要写“圆”类:
    class CPoint { double x,y; }; class
    CCircle:public CPoint { double r; };

    几何形体程序中,需要写“点”类,也需要写“圆”类,两者的关系就是复合关系 ---- 每一个“圆”对象里都包含(有)一个“点”对象,这个“点”对象就是圆心

    class CPoint
    {double x, y;
    };
    class CCircle
    {double r;CPoint center;
    };
    

三、派生类覆盖基类成员&类的保护成员

1.派生类覆盖基类成员

  • 派生类可以定义一个和基类成员同名的成员,这叫覆盖。在派生类中访问这类成员时,缺省的情况是访问派生类中定义的成员。要在派生类中访问由基类定义的同名成员时,要使用作用域符号::。

  • 示例:
    基类定义:

    class base
    {int j;public:int i;void func();
    };
    

    派生类定义:

    class derived : public base
    {public:int i;void access();void func();
    };
    

    一般来说,基类和派生类不定义同名成员变量。

    void derived::access()
    {j = 5;        //errori = 5;        //引用的是派生类的 ibase::i = 5;  //引用的是基类的 ifunc();       //派生类的base::func(); //基类的
    }
    

2.类的保护成员

  • 三种成员变量的访问范围对比

    • 基类的private成员:可以被下列函数访问

      • 基类的成员函数
      • 基类的友元函数
    • 基类的public成员:可以被下列函数访问
      • 基类的成员函数
      • 基类的友元函数
      • 派生类的成员函数
      • 派生类的友元函数
      • 其他的函数
    • 基类的protected成员:可以被下列函数访问
      • 基类的成员函数
      • 基类的友元函数
      • 派生类的成员函数可以访问当前对象的基类的保护成员
  • 保护成员示例:

    class Father
    {private:int nPrivate; //私有成员
    public:int nPublic; //公有成员
    protected:int nProtected; // 保护成员
    };
    class Son : public Father
    {void AccessFather(){nPublic = 1;    // ok;nPrivate = 1;   // wrongnProtected = 1; // OK,访问从基类继承的protected成员Son f;f.nProtected = 1; //wrong ,f不是当前对象}
    };
    int main()
    {Father f;Son s;f.nPublic = 1;    // Oks.nPublic = 1;    // Okf.nProtected = 1; // errorf.nPrivate = 1;   // errors.nProtected = 1; //errors.nPrivate = 1;   // errorreturn 0;
    }
    

四、派生类的构造函数

1.示例说明

  • 以虫子类Bug为基类,派生出飞虫类FlyBug

    • 虫子类:

      class Bug
      {private:int nLegs;int nColor;public:int nType;Bug(int legs, int color);void PrintBug(){};
      };
      
    • 飞虫类:

      class FlyBug : public Bug // FlyBug是Bug的派生类
      {int nWings;public:FlyBug(int legs, int color, int wings);
      };
      
    • 虫子类的构造函数:

      Bug::Bug(int legs, int color)
      {nLegs = legs;nColor = color;
      }
      
    • 飞虫类的错误构造函数示例:

      //错误的FlyBug构造函数
      FlyBug::FlyBug(int legs, int color, int wings)
      {nLegs = legs;   // 不能访问nColor = color; // 不能访问nType = 1;      // oknWings = wings;
      }
      

      派生类成员函数不能访问基类的私有成员

    • 正确的构造函数:

      //正确的FlyBug构造函数:
      FlyBug::FlyBug(int legs, int color, int wings) : Bug(legs, color)
      {nWings = wings;
      }
      

2.包含成员对象的派生类的构造函数写法

  • 将成员对象的初始化放在初始化列表中

  • 示例:

    class Bug
    {private:int nLegs;int nColor;public:int nType;Bug(int legs, int color);void PrintBug(){};
    };
    class Skill
    {public:Skill(int n) {}
    };
    class FlyBug : public Bug
    {int nWings;Skill sk1, sk2;public:FlyBug(int legs, int color, int wings);
    };
    FlyBug::FlyBug(int legs, int color, int wings) : Bug(legs, color), sk1(5), sk2(color), nWings(wings) {}
    

3.封闭派生类对象的构造函数执行顺序

  • 在创建派生类的对象时:

    1. 先执行基类的构造函数,用以初始化派生类对象中从基类继承的成员;
    2. 再执行成员对象类的构造函数,用以初始化派生类对象中成员对象。
    3. 最后执行派生类自己的构造函数

4.封闭派生类对象消亡时析构函数的执行顺序

  • 在派生类对象消亡时:

    1. 先执行派生类自己的析构函数
    2. 再依次执行各成员对象类的析构函数
    3. 最后执行基类的析构函数

五、public继承的赋值兼容规则

class base
{};
class derived : public base
{};
base b;
derived d;

1.public继承的赋值兼容规则

  • 1) 派生类的对象可以赋值给基类对象

    b = d;
    
  • 2) 派生类对象可以初始化基类引用

    base & br = d;
    
  • 3) 派生类对象的地址可以赋值给基类指针

    base * pb = & d;
    

2.protected继承和private继承

  • protected继承时,基类的public成员和protected成员成为派生类的protected成员。
  • private继承时,基类的public成员成为派生类的private成员,基类的protected成员成为派生类的不可访问成员。
  • protected和private继承不是“是”的关系。

3.基类与派生类的指针强制转换

  • 公有派生的情况下,派生类对象的指针可以直接赋值给基类指针

    Base * ptrBase = &objDerived;
    

    ptrBase指向的是一个Derived类的对象;*ptrBase可以看作一个Base类的对象,访问它的public成员直接通过ptrBase即可,但不能通过ptrBase访问objDerived对象中属于Derived类而不属于Base类的成员

  • 即便基类指针指向的是一个派生类的对象,也不能通过基类指针访问基类没有,而派生类中有的成员。

  • 通过强制指针类型转换,可以把ptrBase转换成Derived类的指针

    Base * ptrBase = &objDerived;
    Derived *ptrDerived = (Derived * ) ptrBase;
    

    要保证ptrBase指向的是一个Derived类的对象,否则很容易会出错。

  • 基类与派生类的指针强制转换的解释:

    基类成员变量放在存储位置的起始端:

六、直接基类和间接基类

1.直接基类和间接基类概念

  • 类A派生类B,类B派生类C,类C派生类D,……

    • 类A是类B的直接基类
    • 类B是类C的直接基类,类A是类C的间接基类
    • 类C是类D的直接基类,类A、B是类D的间接基类
  • 在声明派生类时,只需要列出它的直接基类

    • 派生类沿着类的层次自动向上继承它的间接基类
    • 派生类的成员包括
    • 派生类自己定义的成员
    • 直接基类中的所有成员
    • 所有间接基类的全部成员

2.直接基类和间接基类示例程序

  • 代码:

    #include <iostream>
    using namespace std;
    class Base
    {public:int n;Base(int i) : n(i){cout << "Base " << n << " constructed"<< endl;}~Base(){cout << "Base " << n << " destructed"<< endl;}
    };
    class Derived : public Base
    {public:Derived(int i) : Base(i){cout << "Derived constructed" << endl;}~Derived(){cout << "Derived destructed" << endl;}
    };
    class MoreDerived : public Derived
    {public:MoreDerived() : Derived(4){cout << "More Derived constructed" << endl;}~MoreDerived(){cout << "More Derived destructed" << endl;}
    };
    int main()
    {MoreDerived Obj;return 0;
    }
    
  • 输出结果:

    Base 4 constructed
    Derived constructed
    More Derived constructed
    More Derived destructed
    Derived destructed
    Base 4 destructed

【知识索引】【C++入门】

【C++入门】C++ 继承和派生相关推荐

  1. c++的继承与派生之从入门到入坟-------集大成者

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 学习目标 一.为什么使用继承 二.继承的工作方式 1.从基类中派生新类 2.继承下的访问控制 三.派生类对象的初始化和 ...

  2. C++(22)--继承和派生

    继承和派生 1.基本概念 2.实现公有继承 3.私有继承的例子 4. 继承和组合 <老九学堂C++课程><C++ primer>学习笔记.<老九学堂C++课程>详情 ...

  3. 模块的封装之C语言类的继承和派生

    [交流][微知识]模块的封装(二):C语言的继承和派生 在模块的封装(一):C语言的封装中,我们介绍了如何使用C语言的结构体来实现一个类的封装,并通过掩码结构体的方式实 现了类成员的保护.这一部分,我 ...

  4. python基础——继承与派生、组合

    python基础--继承与派生 1 什么是继承: 继承是一种创建新的类的方式,在python中,新建的类可以继承自一个或者多个父类,原始类成为基类或超累,新建的类成为派生类或子类 1.1 继承分为:单 ...

  5. Python基础20-面向对象:静态、组合、继承与派生、多态、封装、反射、内置attr方法等

    目录 静态 静态属性@property 类方法@classmethod 静态方法@staticmethod 组合 继承与派生 继承与派生 继承顺序 在子类中调用父类方法与super 多态 封装 反射 ...

  6. TypeScript基础入门 - 接口 - 继承接口

    转载地址 TypeScript基础入门 - 接口 - 继承接口 项目实践仓库 https://github.com/durban89/typescript_demo.git tag: 1.0.13 为 ...

  7. Python之面向对象继承和派生

    Python之面向对象继承和派生 什么是继承: 继承是一种创建新的类的方法.在Python中,新建的类可以继承自一个或多个父类.原始类称为基类或超类. 新建的类称为派生类或子类. Python中类的继 ...

  8. C++对象内存布局--③测试多继承中派生类的虚函数在哪一张虚函数表中

    C++对象内存布局--③测试多继承中派生类的虚函数在哪一张虚函数表中 测试2:证明派生类的虚函数的地址跟第一基类的虚函数地址保存在同一张虚函数表中. 派生类有多少个拥有虚函数的基类,派生类对象就有多少 ...

  9. JAVA 继承和派生4.1-4.3 2020.3.20

    ** tips 11:08:49 纪明宇老师 13936001804 11:25:04 大家要注意理解: 子类是一种父类 子类对象也是一种父类型的对象 还有间接父类 4.1.1继承的概念 两个类,有些 ...

最新文章

  1. BZOJ 3994: [SDOI2015]约数个数和 [莫比乌斯反演 转化]
  2. python语言程序设计基础第二版第六章答案-Python语言程序设计基础(第2版) 课后题 第六章...
  3. [大牛就是牛]双栈排序
  4. mysql 合计单条数据_mysql之数据去重并记录总数
  5. 【Ionic】---Using Local Notifications In Your Ionic Framework App
  6. “互联网+”大赛全市第三名软件杯全国第一名 - Cloud Lab商业策划书
  7. 论文记载:A Survey on Traffic Signal Control Methods
  8. 常用计算器就是计算机吗,计算器和计算机的区别?
  9. XTU 1339 Interprime
  10. 从羽泉演唱会大数据看在线演出前景
  11. 跨站点请求伪造攻击的原理及防御
  12. 实时互联网的隐形风口
  13. 【C语言程序设计·考试复习】视频讲解课程合集
  14. 计算机自动计算的条件,电脑表格怎样自动计算
  15. iOS 自带地图详解
  16. exls表格搜索快捷键_excel表格中的快捷键
  17. Python+WebKit+HTML开发桌面应用程序
  18. 微信小游戏 - 小游戏 vs H5 游戏性能对比和分析
  19. A375皮肤黑色素瘤细胞膜修饰纳米囊泡|saos2骨肉瘤细胞膜复合纳米脂质体
  20. HEVC代码学习13:predInterSearch函数

热门文章

  1. python识别_识别串口/ usb设备python
  2. CentOS 7 使用ip addr查询不到IP
  3. springmvc04跳转
  4. c语言gets,getc,C语言的getc()函数和gets()函数的使用对比
  5. django配置邮件服务器,python – 使用Bluehost电子邮件服务器的Django电子邮件配置...
  6. python语言中函数在调用前必须先定义吗_应该在python中使用函数之前进行定义?...
  7. 高斯正反算 java_高斯投影正反算的代码
  8. python三天速成_python学习第三天
  9. sql重复数据只保留一条_一条SQL完成跨数据库实例Join查询
  10. 在布局空间标注的尺寸量不对_CAD解决布局标注尺寸不对问题 及快捷键混乱问题...