重学C++系列之继承

news2024/12/29 14:10:51

一、什么是继承

        继承是面向对象三大特性之一,C++中,被继承的类称为基类(父类),继承别的类的类成为派生类(子类),继承除了基类的构造函数和析构函数不继承外,其余成员全部继承。在派生类中继承的成员函数可以重写。通过继承,使得派生类拥有基类的成员。

二、为什么要继承

        通过继承,使得派生类拥有基类的成员,这样可以提高代码的可重用性

三、继承的种类,基本语法是什么

        1、按照继承方式可分为公有继承,保护继承,私有继承

        2、按照继承的基类的个数可分为单继承,多继承。 

        3、按照继承的机制可分为普通继承虚继承。

        基本语法如下:不声明继承方式,默认是私有继承。

class 派生类名:[继承方式] 基类名
{
    // 派生类成员声明

}

四、继承的特性

        1、派生类除了基类的构造函数和析构函数不继承外,其余成员都继承。

        2、派生类对基类成员的初始化默认调用的无参构造函数。

        3、构造函数和析构函数调用顺序如下表

构造函数和析构函数调用顺序
情况构造函数析构函数
只有基类和派生类基类 ---> 派生类派生类 ---> 基类
派生类中有内嵌子对象  基类 ---> 内嵌子对象        --->   派生类派生类 ---> 内嵌子对象 ---> 基类
在基类中有内嵌子对象内嵌子对象 ---> 基类         ---> 派生类派生类 ---> 基类 ---> 内嵌子对象

        4、 基类的成员在派生类中的权限属性如下表

基类的成员在派生类中的权限属性
继承方式\成员publicprotectedprivate
公有继承publicprotected不可见
保护继承protectedprotected不可见
私有继承privateprivate不可见

        在继承中,为了不影响成员的隐藏性,又要提供对基类成员的访问效率,可以将基类的成员变量设置为protected。 

        5、继承中的Shadow(影子)现象。

五、案例

1、构造函数和析构函数调用顺序

(1)只有基类和派生类

#include <iostream>

using namespace std;


// 基类
class Base
{
public:
    Base()
    {
        cout << "Base()" << endl;
    }
    ~Base()
    {
        cout << "~Base()" << endl;
    }

};

// 公有继承
class Test:public Base
{
public:
    Test()
    {
        cout << "Test()" << endl;
    }
    ~Test()
    {
        cout << "~Test()" << endl;
    }
};

int main()
{
    Test a;


    return 0;
}

(2)派生类中内嵌子对象

#include <iostream>

using namespace std;


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


// 基类
class Base
{

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

};

// 公有继承
class Test:public Base
{
    A a;    // 派生类中内嵌子对象
public:
    Test()
    {
        cout << "Test()" << endl;
    }
    ~Test()
    {
        cout << "~Test()" << endl;
    }
};

int main()
{
    Test a;


    return 0;
}

(3)基类中内嵌子对象

#include <iostream>

using namespace std;


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


// 基类
class Base
{
    A a;    // 基类中内嵌子对象
public:
    Base()
    {
        cout << "Base()" << endl;
    }
    
    ~Base()
    {
        cout << "~Base()" << endl;
    }

};

// 公有继承
class Test:public Base
{
public:
    Test()
    {
        cout << "Test()" << endl;
    }
    ~Test()
    {
        cout << "~Test()" << endl;
    }
};

int main()
{
    Test a;


    return 0;
}

        (4)多继承中调用顺序跟继承顺序有关

#include <iostream>

using namespace std;

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

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


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

// 构造函数初始化顺序是 A, B, C, Test
class Test:public A, public B, public C
{
public:
    Test()
    {
        cout << "Test()" << endl;
    }
    ~Test()
    {
        cout << "~Test()" << endl;
    }
};


int main()
{
    Test a;




    return 0;
}

2、基类的成员在派生类中的权限属性

(1) 通过公有继承后

#include <iostream>

using namespace std;


// 基类
class Base
{
private:
    int base_private;
protected:
    int base_protected;
public:
    int base_public;
    Base(int a = 0, int b = 0, int c = 0)
    {
        // cout << "Base()" << endl;
        this->base_private = a;
        this->base_protected = b;
        this->base_public =c;
    }
    
    ~Base()
    {
        // cout << "~Base()" << endl;
    }

    int getPrivate()const
    {
        return base_private;
    }

};

// 公有继承
class Test:public Base
{
public:
    // 派生类的中基类成员的初始化采用,构造函数参数列表的方式
    Test(int a = 0, int b = 0, int c = 0):Base(a, b, c)
    {
        // cout << "Test()" << endl;
    }
    ~Test()
    {
        // cout << "~Test()" << endl;
    }
    void show()const
    {
        // 基类成员的属性为公有的,公有继承后,保持不变
        cout << "public: " << base_public << endl;
        // 基类成员的属性为保护的,公有继承后,保持不变
        cout << "protected: " << base_protected << endl;
        // 基类成员的属性为私有,公有继承后,在派生类和类外都不可见
        // cout << "private: " << base_private << endl;
        cout << "private: " << getPrivate() << endl;
    }

};

int main()
{
    Test a(1, 10, 100);
    a.show();
    cout << "public: " << a.base_public << endl;    // 公有,类外也可以访问


    return 0;
}

(2) 通过保护继承后

#include <iostream>

using namespace std;


// 基类
class Base
{
private:
    int base_private;
protected:
    int base_protected;
public:
    int base_public;
    Base(int a = 0, int b = 0, int c = 0)
    {
        // cout << "Base()" << endl;
        this->base_private = a;
        this->base_protected = b;
        this->base_public =c;
    }
    
    ~Base()
    {
        // cout << "~Base()" << endl;
    }

    int getPrivate()const
    {
        return base_private;
    }

};

// 保护继承
class Test:protected Base
{
public:
    // 派生类的中基类成员的初始化采用,构造函数参数列表的方式
    Test(int a = 0, int b = 0, int c = 0):Base(a, b, c)
    {
        // cout << "Test()" << endl;
    }
    ~Test()
    {
        // cout << "~Test()" << endl;
    }
    void show()const
    {
        // 基类成员的属性为公有的,保护继承后,属性变成保护
        cout << "public: " << base_public << endl;
        // 基类成员的属性为保护的,保护继承后,保持不变
        cout << "protected: " << base_protected << endl;
        // 基类成员的属性为私有,保护继承后,在派生类和类外都不可见
        // cout << "private: " << base_private << endl;
        cout << "private: " << getPrivate() << endl;
    }

};

int main()
{
    Test a(1, 10, 100);
    a.show();
    // cout << "public: " << a.base_public << endl;    // 保护,类外不可以访问


    return 0;
}

(3) 通过私有承后

#include <iostream>

using namespace std;


// 基类
class Base
{
private:
    int base_private;
protected:
    int base_protected;
public:
    int base_public;
    Base(int a = 0, int b = 0, int c = 0)
    {
        // cout << "Base()" << endl;
        this->base_private = a;
        this->base_protected = b;
        this->base_public =c;
    }
    
    ~Base()
    {
        // cout << "~Base()" << endl;
    }

    int getPrivate()const
    {
        return base_private;
    }

};

// 保护继承
class Test:private Base
{
public:
    // 派生类的中基类成员的初始化采用,构造函数参数列表的方式
    Test(int a = 0, int b = 0, int c = 0):Base(a, b, c)
    {
        // cout << "Test()" << endl;
    }
    ~Test()
    {
        // cout << "~Test()" << endl;
    }
    void show()const
    {
        // 基类成员的属性为公有的,私有继承后,属性变成私有
        cout << "public: " << base_public << endl;
        // 基类成员的属性为保护的,私有继承后,属性变成私有
        cout << "protected: " << base_protected << endl;
        // 基类成员的属性为私有,私有继承后,在派生类和类外都不可见
        // cout << "private: " << base_private << endl;
        cout << "private: " << getPrivate() << endl;
    }

};

int main()
{
    Test a(1, 10, 100);
    a.show();
    // cout << "public: " << a.base_public << endl;    // 私有,类外不可以访问


    return 0;
}

3、继承中的Shadow(影子)现象

        在继承中,当派生类中出现与基类同名成员时,默认指定是派生类中的成员,如果要指定是基类的成员,必须加作用域运算符::,如基类::成员。

        在单继承中,出现影子现象可以编译通过;而在多继承中,出现影子现象编译不通过,产生二义性。

#include <iostream>

using namespace std;

class base
{
private:
    int a;

public:
    base(int a = 0)
    {
        this->a = a;
    }
    void show()
    {
        cout << "base::show()" << endl;
    }
};

class Test:public base
{
public:
    Test(int a = 0):base(a)
    {

    }
    void show()
    {
        cout << "Test::show()" << endl;
    }

};


int main()
{
    Test a(10);
    a.show();   // 调用Test::show()
    a.base::show(); // 调用base::show()

    return 0;
}

六、总结

        本文讲解继承的知识,包括继承的概念,调用构造函数和析构函数的顺序,基类成员在派生类中的权限属性和继承中的影子现象。其实还有虚继承,放在另外一篇讲,感兴趣的搜索左边的目录栏就可以找到。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【安卓】视频播放器实现过程,超详细注释,自定义视频进度条,打开本地文件播放视频等功能。

一、实现效果 废话不多说&#xff0c;直接上代码&#xff0c;里面有详细注释&#xff0c;不清楚的评论区留言。 二、布局代码 <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res…

Android安卓实战项目(3)—一个炫酷的健身APP界面

Android安卓实战项目&#xff08;3&#xff09;—一个炫酷的健身APP界面 一.项目运行介绍 1.大致浏览 2.功能介绍 &#xff08;1&#xff09;功能一 上导航条 &#xff08;2&#xff09;功能二 下导航条 二.具体实现 MainActivity.java package com.rckdeveloper.fitene…

python 面向对象编程的特点 - 封装 - 继承(经典类、新式类) - 多态 - 静态方法、类方法 - 下划线的使用 - 回合制攻击游戏实验

目录 面向对象编程的特点&#xff1a; 封装&#xff1a;封装是将数据和操作&#xff08;方法&#xff09;封装在一个对象中的能力 继承&#xff1a;继承是指一个类&#xff08;子类&#xff09;可以继承另一个类&#xff08;父类&#xff09;的属性和方法。 我们为什么需要继…

Python采集法外狂徒张三所有视频【含jS逆向解密】

传说中&#xff0c;有人因为只是远远的看了一眼法外狂徒张三就进去了&#x1f602; 我现在是获取他视频&#xff0c;岂不是直接终生了&#x1f929; 网友&#xff1a;赶紧跑路吧 &#x1f60f; 好了话不多说&#xff0c;我们直接开始今天的内容吧&#xff01; 你需要准备 …

详解STM32的GPIO八种输入输出模式,GPIO各种输入输出的区别、初始化的步骤详解,看这文章就行了(超详细)

在STM32微控制器中&#xff0c;常见的输入输出(GPIO)模式有八种&#xff0c;分别是推挽输出、开漏输出、复用推挽输出、复用开漏输出、浮空输入、上拉输入、下拉输入和模拟输入。下面我将为你解释每种模式的特点和区别&#xff0c;并提供相应的示例代码。 文章目录 介绍区别初…

组合模式-树形结构的处理

A公司需要筛选出年龄35岁及以上(如果是领导&#xff0c;年龄为45岁及以上)的人。其组织架构图如下。 图 A公司部分组织架构图 图 传统解决方案 public class Development {private String name;public Development(String name) {this.name name;}List<Employee> emplo…

uni-app优雅的实现时间戳转换日期格式

现在显示的格式如下图&#xff1a; 我期望统一格式&#xff0c;所以不妨前端处理一下&#xff0c;核心代码如下 filters: {// 时间戳处理formatDate: function(value, spe /) {value value * 1000let data new Date(value);let year data.getFullYear();let month data.…

【设计模式——学习笔记】23种设计模式——适配器模式Adapter(原理讲解+应用场景介绍+案例介绍+Java代码实现)

介绍 生活中的案例 不同国家的插座不同&#xff0c;出国旅游充电器不能直接使用&#xff0c;可以通过使用多功能转换插头来辅助使用 基础介绍 适配器模式将某个类的接口转换成客户端期望的另一个接口表示&#xff0c;主的目的是兼容性&#xff0c;让原本因接口不匹配不能一起…

github gitlab 多用户多平台切换

一、背景 我需要用账号1 来登录并管理github 账号 我需要用账号2 来登录并管理gitlab 账号 二、设置账号 邮箱 设置账号1用户名与邮箱 git config --global user.name "miaojiang" git config --global user.email "187133163.com" 三、生成本地密钥…

LT6911C 是一款HDMI 1.4到双端口MIPIDSI/CSI或者LVDS加音频的一款高性能芯片

LT6911C 1.描述&#xff1a; LT6911C是一款高性能的HDMI1.4到MIPIDSI/CSI/LVDS芯片&#xff0c;用于VR/智能手机/显示器应用程序。对于MIPIDSI/CSI输出&#xff0c;LT6911C具有可配置的单端口或双端口MIPIDSI/CSI&#xff0c;具有1个高速时钟通道和1个~4个高速数据通道&#…

ChatGLM-RM(Reward Model)实现代码逐行讲解

这里我们尝试通过RM训练让模型学会从给定上下文中提取信息&#xff0c;来进行RM模型的实践。你可以从下面链接获取代码 GitHub - Pillars-Creation/ChatGLM-RLHF-LoRA-RM: ChatGLM-6B添加了RLHF的实现&#xff0c;以及部分核心代码的逐行讲解 ,实例部分是做了个新闻短标题的生成…

入行软件测试7年,才知道原来字节跳动这么容易进

当前就业环境&#xff0c;裁员、失业消息满天飞&#xff0c;好像有一份工作就不错了&#xff0c;更别说高薪了。其实这只是一方面&#xff0c;而另一方面&#xff0c;各大企业依然求贤若渴&#xff0c;高技术人才依然紧缺&#xff0c;只要你技术过硬&#xff0c;拿个年薪50w不是…

FUNBOX_1靶场详解

FUNBOX_1靶场复盘 这个系列的靶场给出的干扰因素都挺多的&#xff0c;必须从中找到有用的线索才可以。 这个靶场你扫描到ip地址后打开网页会发现&#xff0c;ip自动转换成域名了&#xff0c;所以我们需要添加一条hosts解析才可以。 192.168.102.190 funbox.fritz.box从目录…

4EVERLAND 托管让 Permaweb 变得更容易!

在互联网托管领域&#xff0c;我们通常将其与存储和管理网站的服务联系起来。传统的 Web2 托管服务在集中式服务器模型上运行&#xff0c;其中网站文件和数据库存储在集中管理的服务器上。用户通过互联网访问网站。这种托管模式应用广泛&#xff0c;相对简单&#xff0c;适合很…

计算机存储结构、执行速度及对应用的影响

万丈高楼&#xff0c;平地起。 计算机世界的信息化软件工程&#xff0c;是构筑于计算机硬件之上的。 由于信息的流转依托于计算机不同的部件&#xff0c;所以计算机系统的内部设计、各类应用架构无不受部件之间速度差异的影响。 本文&#xff0c;主要先介绍存储体系&#xff0c…

Spring Security 授权体系结构

目录 1、Authorities 授权&#xff08;AuthorizationFilter 过滤器&#xff09; 2、AuthorizationManager 授权管理器 3、角色的层次化&#xff08;Roles&#xff09; 1、Authorities 授权&#xff08;AuthorizationFilter 过滤器&#xff09; 通过 Authentication.getAutho…

Spring基础知识讲解

文章目录 Spring是什么&#xff1f;IoC容器与DIDI与IoC的区别 Spring项目的创建配置maven国内源创建Spring项目有关Bean的操作存储Bean使用Bean ApplicationContext和BeanFactory的区别getBean()的三种使用方法 更简单的存储和获取对象类注解方法注解获取Bean对象的简单方法——…

Ubuntu18.04下安装ROS

安装相关依赖 sudo apt install ninja-build exiftool ninja-build protobuf-compiler libeigen3-dev genromfs xmlstarlet libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev python-pip python3-pip ----------------------------------------------------------------…

TCP/IP 介绍

一、TCP/IP 是什么 TCP /IP 是因特网通信协议 &#xff08;注释&#xff1a;通信协议是对计算机必须遵守的规则的描述&#xff0c;只有遵守这些规则&#xff0c;计算机之间才能进行通信。&#xff09; 因特网浏览器和因特网服务器均使用 TCP/IP 来连接因特网。浏览器使用 TCP…

Idea 避免import *

File -> setting -> Editor -> Code Style -> Java -> Imports