C++面向对象核心-继承

1、继承

1.1 概念

继承是面向对象的三大特性之一,体现了代码复用的思想。

继承就是在一个已存在的类的基础上建立一个新的类,并拥有其特性。

  • 已存在的类被称为“基类”或者“父类”
  • 新建立的类被称为“派生类”或者“子类”
  • 对象间没有继承关系

#include <iostream>

using namespace std;

// 基类
class Father
{
private:
    string name = "孙";
public:
    void set_name(string name)
    {
        this->name = name;
    }

    string get_name()
    {
        return name;
    }

    void work()
    {
        cout << "我的工作是厨师,我负责炒菜" << endl;
    }
};

// 派生类
class Son:public Father
{

};

int main()
{
    Son son;
    cout << son.get_name() << endl;
    son.work();

    return 0;
}

派生类差异化

上面的代码,Son类的功能几乎与Father类重叠,在实际的使用过程中,派生类会做出一些与基类的差异化。

  • 修改继承过来的基类内容

属性:1、公有的属性可以直接更改。2、私有的属性,需要使用基类提供的公有函数,进行更改。

成员函数:函数隐藏,通过派生类实现一个同名同参的函数,来隐藏基类的函数。

调用隐藏函数,需要指定作用域

  • 新增派生类的内容

#include <iostream>
#include <array>
#include <vector>
#include <list>
#include <deque>
#include <map> // 头文件
using namespace std;
class Father
{
private:
    string name = "孙";
public:
    void set_name(string name)
    {
        this->name = name;
    }
    string get_name()
    {
        return name;
    }
    void work()
    {
        cout<<"我的工作是厨师,我负责炒菜 "<<endl;
    }
};
//派生类
class Son:public Father
{
 public:
    void init()
    {
        set_name("王");
    }
    //函数隐藏,隐藏父类同名函数
    void work()
    {
        cout<<"我的工作是学生"<<endl;
    }
    void game()
    {
        cout<<"我还玩游戏"<<endl;
    }
};
int main()
{
    Son son;//无参构造函数
    cout << son.get_name()<<endl;//孙
    son.work();//我的工作是学生
    son.game();//我还玩游戏
    son.init();
    cout << son.get_name()<<endl;//王
    son.Father::work();//”我的工作是厨师,我负责炒菜“;调用隐藏函数,需要指定作用域
    return 0;
}

派生类往往是类的具象化,基类则是派生类的抽象。

基类和派生类是相对的,一个类可能又存在基类,又存在派生的情况,取决于那两个类进行比较。

1.2 构造函数

1.2.1 派生类与基类构造函数的关系

构造函数和析构函数不能被继承。

#include <iostream>
using namespace std;
// 基类
class Father
{
private:
    string name;
public:
    // 有参构造函数
    Father(string name):name(name){}

    string get_name()
    {
        return name;
    }
};
// 派生类
class Son:public Father
{
public:
    // 编译器自动添加的构造函数,若没有该构造函数,编译器自动添加
    Son():Father(){}
};
int main()
{
    Son son;//创建对象时发现该类由基类,则找基类构造对象
    cout << son.get_name() << endl;
    return 0;
}

派生类的任意一个构造函数,都必须直接或者间接调用基类的任意构造函数。

1.2.2 解决方案

1.2.2.1 补充基类的无参构造函数

#include <iostream>
using namespace std;
// 基类
class Father
{
private:
    string name;
public:
    Father()
    {
        name  = "王";
    }

    // 有参构造函数
    Father(string name):name(name){}

    string get_name()
    {
        return name;
    }
};
// 派生类
class Son:public Father
{
public:
    // 编译器自动添加的构造函数
    Son():Father(){}
};
int main()
{
    Son son;
    cout << son.get_name() << endl;

    return 0;
}


1.2.2.2 手动在派生类中调用基类构造函数
1.2.2.2.1 透传构造

在派生类的构造函数中,调用基类的构造函数,实际上编译器自动添加派生类的构造函数,调用基类无参构造时,采用的就是这种方式。

#include <iostream>
using namespace std;
// 基类
class Father
{
private:
    string name;
public:
//    Father()
//    {
//        name  = "王";
//    }
    // 有参构造函数
    Father(string name):name(name){}
    string get_name()
    {
        return name;
    }
};
// 派生类
class Son:public Father
{
public:
    // 编译器自动添加的构造函数,透传构造
    // Son():Father(){}
    // 手动添加的构造函数,透传构造
    Son():Father("张"){}
    // 手动添加派生类的有参构造函数
    Son(string name):Father(name){}
};
int main()
{
    Son son("王");
    cout << son.get_name() << endl;
    return 0;
}

1.2.2.2.2 委托构造

一个类的构造函数可以调用这个类中的另一个构造函数。要避免循环委托。

委托构造的性能低于透传构造,但是代码的维护性更好。因为通常一个类中构造函数都会委托给能力最强(参数最多)的构造函数。(效率低)代码重构时,只需要更改这个能力最强的构造函数即可。

#include <iostream>
using namespace std;
// 基类
class Father
{
private:
    string name;
public:
//    Father()
//    {
//        name  = "王";
//    }
    // 有参构造函数
    Father(string name):name(name){}
    string get_name()
    {
        return name;
    }
};
// 派生类
class Son:public Father
{
public:
    // 编译器自动添加的构造函数,透传构造
    // Son():Father(){}
    // 手动添加的构造函数,透传构造
    Son():Son("张"){}
    // 手动添加派生类的有参构造函数
    Son(string name):Father(name){}
};
int main()
{
    Son son("王");
    cout << son.get_name() << endl;

    return 0;
}

#include <iostream>

using namespace std;

// 基类
class Father
{
private:
    string name;
    int i;
public:

    // 有参构造函数

    Father(string name,int i):name(name),i(i){}
    string get_name()
    {
        return name;
    }

    int get_i()
    {
        return i;
    }


};

// 派生类继承基类  公有继承
class Son:public Father
{
public:
    // 编译器自动添加的构造函数,透传构造
    // Son():Father(){}

    // 手动添加构造函数,委托构造
    Son():Son("张"){}

    Son(string name,int i = 1):Father(name,i){}

};

int main()
{
    Son son("hello",2);
    cout << son.get_name() << endl;
    cout << son.get_i() << endl;

    return 0;
}

1.2.2.2.3 继承构造

只需要加这一句话,编译器就会添加上面两种构造函数(using Father::Father;)

C++11新增的写法,只需要一句话,就可以自动给派生类添加n个(n为基类构造函数的个数)构造函数。并且每个派生类的构造函数与基类的构造函数格式相同,每个 派生类的构造函数都通过透传构造调用对应格式的基类构造函数。

#include <iostream>
using namespace std;
//基类
class Father
{
private:
    string name;
public:
    //委托构造
    Father():Father("王"){}
    //有参函数构造
    Father(string name):name(name){}
    string get_name()
    {
        return name;
    }
};
//派生类
class Son:public Father
{
public:
    //    //编译器自动添加的构造函数,透传构造
    //    //Son():Father(){}
    //    //手动添加的构造函数,透传构造
    //    Son():Father("张"){}
    //    //手动添加派生类的有参构造函数
    //    Son(string name):Father(name){}
    //只需要加这一句话,编译器就会添加上面两种构造函数
    using Father::Father;
};
int main()
{
    Son son;//无参构造函数
    Son son1("张");
    cout << son.get_name()<<endl;
    cout << son1.get_name()<<endl;
    return 0;
}

1.3 对象的创建与销毁流程

在继承中,构造函数与析构函数的调用。

#include <iostream>

using namespace std;

class Value
{
private:
    string str;
public:
    Value(string str):str(str)
    {
        cout << str <<"构造函数" << endl;
    }

    ~Value()
    {
        cout << str << "析构函数" << endl;
    }
};

class Father
{
public:
    static Value s_value;
    Value val = Value("Father 成员变量");
    Father()
    {
        cout << "Father 构造函数被调用了" << endl;
    }
    ~Father()
    {
        cout << "Father 析构函数被调用了" << endl;
    }
};
Value Father::s_value = Value("静态FatherValue创建了");

class Son:public Father
{
public:
    static Value s_value;
    Value val = Value("Son 成员变量");

    Son()
    {
        cout << "Son构造函数被调用了" << endl;
    }
    ~Son()
    {
        cout << "Son析构函数被调用" << endl;
    }

};
Value Son::s_value = Value("静态SonValue被创建了");

int main()
{
    cout << "主程序开始执行" << endl;
    // 局部代码块
    {
        Son s;
        cout << "对象执行中" << endl;
    }
    cout << "主程序结束了" << endl;
    return 0;
}

上面的执行结果中,可以得到下面的规律:

  • 以“对象执行中”为轴,上下对称,先创建静态后创建非静态,先创建基类,后创建派生类,先创建成员变量,后创建对象。先析构成员变量后析构函数,先析构派生类,后析构基类,先析构非静态,后析构静态。
  • 在创建的过程中,同类型的内存区域,基类先开辟。
  • 静态的创建早于非静态
  • 对象的创建,晚于类中成员变量的创建。

1.4 多重继承

1.4.1 概念

C++支持多重继承,即一个派生类可以有多个基类。派生类对于每个基类的关系仍然可以看作是一个单继承。

#include <iostream>
using namespace std;
class Sofa
{
public:
    void sit()
    {
        cout << "沙发可以坐着" << endl;
    }
};
class Bed
{
public:
    void lay()
    {
        cout << "床可以躺着" << endl;
    }
};
class SofaBed:public Sofa,public Bed
{
   public:
};
int main()
{
    SofaBed sb;
    sb.sit();
    sb.lay();
    return 0;
}

1.4.2 可能出现的问题

1.4.2.1 问题1-重名问题

当多个基类具有重名成员时,编译器在调用的过程中会出现二义性。

解决方式:使用基类的类名::方式调用。

#include <iostream>
using namespace std;
class Sofa
{
public:
    void sit()
    {
        cout << "沙发可以坐着" << endl;
    }
    void test()
    {
        cout << "打扫沙发" << endl;
    }
};
class Bed
{
public:
    void lay()
    {
        cout << "床可以躺着" << endl;
    }
    void test()
    {
        cout << "打扫床" << endl;
    }
};
class SofaBed:public Sofa,public Bed
{
   public:
};
int main()
{
    SofaBed sb;
    sb.sit();
    sb.lay();
//    sb.test(); // 错误二义性,两个类中都继承到相同的成员函数名
    sb.Bed::test();//作用域指定
    sb.Sofa::test();
    return 0;
}

1.4.2.2 问题2-菱形继承

当一个派生类有多个基类,且这些基类又有同一个基类,就会出现二义性问题,这种情况也被称为菱形继承(钻石)继承。

1)作用域加类名解决

#include <iostream>

using namespace std;

class Furniture
{
public:
    void func()
    {
        cout << "家具厂里有家具" << endl;
    }

};
class Sofa:public Furniture
{
public:
};

class Bed:public Furniture
{
public:
};

class SofaBed:public Sofa,public Bed
{
   public:
};


int main()
{
    SofaBed sb;
//    sb.func();
//作用域加类名完成
    sb.Sofa::func();
    sb.Bed::func();
    return 0;
}

2)使用虚继承virtual

虚继承解决二义性是通过虚基类指针和虚基类表实现的。在下面代码中Sofa和Bed类会创建一个虚基类指针和虚基类表。所有同类型对象共用一张虚基类表,每个对象内部增加一个隐藏的虚基类指针成员变量,这个虚基类指针指向虚基类表。当Sofa和Bed类作为基类有了派生类SofaBed时,SofaBed对象中也会有隐藏的虚基类指针,但是SofaBed没有自己的虚基类表。在调用Furniture的成员时,SofaBed对象会通过虚基类指针找到对应的虚基类表,通过查表避免二义性。

#include <iostream>

using namespace std;

class Furniture
{
public:
    void func()
    {
        cout << "家具厂里有家具" << endl;
    }

};
// 虚继承
class Sofa:virtual public Furniture
{
public:
};

class Bed:virtual public Furniture
{
public:
};

class SofaBed:public Sofa,public Bed
{
   public:
};


int main()
{
    SofaBed sb;
    sb.func();
    return 0;
}

练习多重继承:

定义学生类,有姓名,学号,性别,年龄等私有成员变量,有构造函数,有打印信息的成员函数。

要求通过构造函数可以给属性赋予初始值。

定义大学生类,继承自学生类,大学生有专业名、成绩的私有成员变量,还有是否获得奖学金的成员函数(成绩为判断依据)。隐藏基类打印信息的成员函数,新的打印信息的成员函数也要能打印姓名、学号、性别、年龄信息。

要求通过构造函数可以给属性赋予初始值。

再定义研究生类,继承自大学生类,有导师姓名和工资的私有成员变量,有打印工资这个成员函数。

要求通过构造函数可以给属性赋予初始值。

#include <iostream>

using namespace std;
class Student
{
private:
    string name;
    int stdnum;
    string sex;
    int age;
public:
    //有参构造
    Student(string name,int stdnum,string sex,int age):name(name),stdnum(stdnum),sex(sex),age(age){}
    void show_student()
    {
        cout<<"姓名:"<<name<<endl<<"学号:"<<stdnum<<endl<<"性别:"<<sex<<endl<<"年龄 "<<age<<endl;
    }
    string get_name()
    {
        return name;
    }
};
class Undergraduate:public Student
{
private:
    string major;
    float grade;
public:
//透传构造
    Undergraduate(string name,int stdnum,string sex,int age,string major,float grade):Student(name,stdnum,sex,age),major(major),grade(grade){}
    //奖学金判断
    void jodge_scholarship()
    {
        if(this->grade>=95)
        {
            cout<<Student::get_name()<<"获得奖学金"<<endl;
        }
        else
        {
            cout<<Student::get_name()<<"未获得奖学金"<<endl;
        }
    }
    //打印大学生信息
    void show_undergraduate()
    {
        Student::show_student();
        cout<<"专业:"<<major<<endl<<"成绩:"<<grade<<endl;
    }
};
class Graduate:public Undergraduate
{
private:
    string totor;//导师
    int pay;
public:
    //构造
    Graduate(string name,int stdnum,string sex,int age,string major,float grade,string totor,int pay):Undergraduate(name,stdnum,sex,age,major,grade),totor(totor),pay(pay){}
    void get_pay()
    {
        cout<<"薪资待遇:"<<pay<<endl;
    }
    void show_graduate()
    {
        cout<<"导师:"<<totor<<endl;
        cout<<"薪资:"<<pay<<endl;
    }

};

int main()
{
    cout<<"学生类"<<endl;
    Student std1("张三",123,"男",18);
    std1.show_student();
    cout<<"大学生类"<<endl;
    Undergraduate std2("张三",123,"男",18,"嵌入式",99);
    std2.show_undergraduate();
    std2.jodge_scholarship();
    cout<<"研究生类"<<endl;
    Graduate std3("张三",123,"男",18,"嵌入式",99,"沈子",9999);
    std3.show_student();
    std3.show_graduate();
    std3.get_pay();
    return 0;
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/298755.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据恢复与并发控制例题

例1: (1)重做&#xff08;REDO&#xff09;&#xff1a;T1,T2,T3; 撤销&#xff08;UNDO&#xff09;&#xff1a;T4。 (2)重做&#xff1a;T1,T2&#xff1b; 撤销&#xff1a;T3。 (3)重做&#xff1a;T1; 撤销&#xff1a;T2,T3. (4)重做&#xff1a;T1&#xff1b; 撤销…

手机上下载 Linux 系统

我们首先要下载 Ternux 点击下载以及vnc viewer (提取码&#xff1a;d9sX)&#xff0c;需要魔法才行 下载完以后我们打开 Ternux 敲第一个命令 pkg upgrade 这个命令是用来跟新软件的 敲完命令就直接回车&#xff0c;如果遇到需要输入 Y/N 的地方全部输入 Y 下一步 #启动TMOE…

java SSM问卷调查系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM问卷调查管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代 码和数据库&#xff0c;系统主要采…

vite + vue3引入ant design vue 报错

npm install ant-design-vue --save下载插件并在main.ts 全局引入 报错 解决办法一&#xff1a; main.ts注释掉全局引入 模块按需引入 解决办法二 将package.json中的ant-design-vue的版本^4.0.0-rc.4改为 ^3.2.15版本 同时将将package-lock.json中的ant-design-vue的版本…

华为云服务器试用领取

系列文章目录 华为云服务器试用领取 领取的试用云服务器在哪 文章目录 系列文章目录介绍 介绍 我将会用该系列文章讲述如何在云服务器中安装大数据软件及其环境搭建。如有不足之处&#xff0c;还望指点。 本篇文章讲述的是华为云服务器的免费试用。 华为弹性云服务器 ECS 该云…

metaSPAdes,megahit,IDBA-UB:宏基因组装软件安装与使用

metaSPAdes,megahit,IDBA-UB是目前比较主流的宏基因组组装软件 metaSPAdes安装 GitHub - ablab/spades: SPAdes Genome Assembler #3.15.5的预编译版貌似有问题&#xff0c;使用源码安装试试 wget http://cab.spbu.ru/files/release3.15.5/SPAdes-3.15.5.tar.gz tar -xzf SP…

数据分析——快递电商

一、任务目标 1、任务 总体目的——对账 本项目解决同时使用多个快递发货&#xff0c;部分隔离区域出现不同程度涨价等情形下&#xff0c;如何快速准确核对账单的问题。 1、在订单表中新增一列【运费差异核对】来表示订单运费实际有多少差异&#xff0c;结果为数值。 2、将…

【书生·浦语大模型实战营02】《轻松玩转书生·浦语大模型趣味Demo》学习笔记

《轻松玩转书生浦语大模型趣味Demo》 1、InternLM-Chat-7B 智能对话&#xff1a;生成 300 字的小故事 本节中我们将使用InternLM-Chat-7B 模型部署一个智能对话 Demo。 1.1 环境准备 在InternStudio平台中选择 A100(1/4) 的配置&#xff0c;镜像选择 Cuda11.7-conda&#x…

idea中使用Lombok 失效,@Slf4j 找不到符号的解决办法

文章目录 一、前言二、问题排查和解决方案三、 其他解决方案3.1 另一种解决方案3.2 参考文章 一、前言 今天在一个多module工程中&#xff0c;新增了一个 springboot&#xff08;版本 2.2.4.RELEASE&#xff09; module&#xff0c;像往常一样&#xff0c;我引入了lombok依赖&…

电脑开启虚拟化如何查看自己的主机主板型号

问题描述 在使用virtualbox、vmware安装虚拟机的时候&#xff0c;需要本机电脑能够支持虚拟化。 但是不同厂家的主机&#xff08;主板&#xff09;幸好并不一致&#xff0c;所以需要先了解自己的电脑主板型号 操作方法 1、win r 键打开运行窗口&#xff0c;输入cmd并确定打开…

关于“Python”的核心知识点整理大全64

目录 20.2.15 确保项目的安全 settings.py 20.2.16 提交并推送修改 20.2.17 创建自定义错误页面 1. 创建自定义模板 500.html settings.py settings.py 注意 views.py 20.2.18 继续开发 往期快速传送门&#x1f446;&#xff08;在文章最后&#xff09;&#xff1a…

大数据Doris(五十一):Colocation Join介绍

文章目录 Colocation Join介绍 一、原理 二、使用方式 1、建表 2、删表

【Java EE初阶七】多线程案例(生产者消费者模型)

1. 阻塞队列 队列是先进先出的一种数据结构&#xff1b; 阻塞队列&#xff0c;是基于队列&#xff0c;做了一些扩展&#xff0c;适用于多线程编程中&#xff1b; 阻塞队列特点如下&#xff1a; 1、是线程安全的 2、具有阻塞的特性 2.1、当队列满了时&#xff0c;就不能往队列里…

MATLAB插值函数

一、MATLAB插值函数概览 1&#xff09;本节重点介绍的插值函数 MATLAB插值函数适用情况基础句式interp1 函数interp1 主要用于一维数据的插值interp1(x, y, x_interp, ‘linear’); 其中 x 和 y 是已知数据点&#xff0c;x_interp 是要插值的目标点。interp2 函数interp2 用于…

VS code的使用介绍

VS code的使用介绍 简介下载和安装常用的插件使用教程快捷键 集成Git未找到 Git。请安装 Git&#xff0c;或在 "git.path" 设置中配置。操作步骤打开文件夹初始化仓库文件版本控制状态提交文件到git打开git操作栏位 好用的插件ChineseDraw.io Integration实体关系 Gi…

SpringSecurity集成JWT实现后端认证授权保姆级教程-环境搭建篇

&#x1f341; 作者&#xff1a;知识浅谈&#xff0c;CSDN签约讲师&#xff0c;CSDN博客专家&#xff0c;华为云云享专家&#xff0c;阿里云专家博主 &#x1f4cc; 擅长领域&#xff1a;全栈工程师、爬虫、ACM算法 &#x1f492; 公众号&#xff1a;知识浅谈 &#x1f525;网站…

C++ UTF-8与GBK字符的转换 —基于Linux 虚拟机 (iconv_open iconv)

1、UTF-8 和 GBK 的区别 GBK&#xff1a;通常简称 GB &#xff08;“国标”汉语拼音首字母&#xff09;&#xff0c;GBK 包含全部中文字符。 UTF-8 &#xff1a;是一种国际化的编码方式&#xff0c;包含了世界上大部分的语种文字&#xff08;简体中文字、繁体中文字、英文、…

Android 15即将到来,或将推出5大新功能特性

Android15 OneUI电池优化 三星最近完成了对其所有设备的稳定版 One UI 6.0 更新的推出&#xff0c;引起了用户的极大兴奋。据新出现的互联网统计数据显示&#xff0c;即将发布的基于 Android 15 的 One UI 7 将通过优化电池和功耗来重新定义用户体验&#xff0c;这是一项具有突…

[AutoSar]基础部分 RTE 04 数据类型的定义及使用

目录 关键词平台说明一、数据类型分类二、Adt三、Idt四、Base 数据类型五、units六、compu methods七、data constraint 关键词 嵌入式、C语言、autosar、Rte 平台说明 项目ValueOSautosar OSautosar厂商vector芯片厂商TI编程语言C&#xff0c;C编译器HighTec (GCC) 一、数据…

FineBI实战(2):案例架构说明及数据准备

1 系统架构 基于MySQL搭建数据仓库基于Kettle进行数据处理帆软FineBI基于MySQL搭建的数据仓库进行数据分析 2 数据流程图 通过Kettle将MySQL业务系统数据库中&#xff0c;将数据抽取出来&#xff0c;然后装载到MySQL数据仓库中。编写SQL脚本&#xff0c;对MySQL数据仓库中的数…