C++(学习)2024.9.25

news/2024/12/22 1:23:08/

目录

继承

概念

构造函数

1.派生与基构造函数的关系

2.解决方案

(1)补充基的无参构造函数

(2)手动在派生中调用基构造函数

1.透传构造

2.委托构造

3.继承构造

3.对象的创建与销毁流程

4.多重继承

(1)概念

(2)可能出现的问题

1.重名问题

2.菱形继承

权限

1.权限修饰符

2.不同权限的继承

(1)公有继承

(2)保护继承

(3)私有继承


继承

概念

继承就是面向对象的三大特性之一,体现了代码复用的思想。
继承就是在一个已存在的的基础上,建立一个新的,并拥有其特性。
1.已存在的被称为“基”或者“父
2.新建立的被称为“派生”或者“子

#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>
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;son.init();cout << son.get_name() << endl;son.work(); son.game();son.Father::work();return 0;
}

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

构造函数

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
{
};int main()
{
//    Son s;    // 找不到基的无参构造函数
//    Son s("张"); // 找不派生的有参构造函数return 0;
}

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:
};int main()
{Son s;    cout << s.get_name() << endl;return 0;
}
(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(){}// 手动添加构造函数,透传构造Son():Father("王"){}// 有参构造函数,调用基有参构造函数Son(string fn):Father(fn){}
};int main()
{Son s;cout << s.get_name() << endl;Son s1("张");cout << s1.get_name() << endl;return 0;
}
2.委托构造

一个的构造函数可以调用这个的另一个构造函数,但是需要避免循环委托。

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

#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():Son("李"){}    // 委托构造Son(string fn):Father(fn){}    // 有参构造函数,调用基有参构造函数
};int main()
{Son s;cout << s.get_name() << endl;Son s1("赵");cout << s1.get_name() << endl;return 0;
}
3.继承构造

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:using Father::Father;// 只需要补充这一句话,编译器就会自动添加下面两种构造函数    // Son():Father(){}// Son(string fn):Father(fn){}
};int main()
{Son s;cout << s.get_name() << endl;Son s1("王");cout << s1.get_name() << endl;return 0;
}

3.对象的创建与销毁流程

在继承中,构造函数与析构函数是有一定调用顺序的。

#include <iostream>
using namespace std;class Value
{
private:string str;
public:Value(string str):str(str){cout << str<< " 调用Value构造函数" << endl;}~Value(){cout << str << " 调用Value析构函数" << 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)静态的创建早于非静态。
(2)在创建的过程中,同型的内存空间基先开辟,派生先销毁。创建对象时:先基后派。销毁对象时:先派后基
(3)以“对象执行中为轴”上下对称

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
{
};int main()
{SofaBed sobe;sobe.lay();sobe.sit();return 0;
}
(2)可能出现的问题
1.重名问题

当多个基具有重名成员时,编译器在编译的时候会出现二义性问题。
解决方法:使用名::方式调用

#include <iostream>
using namespace std;class Sofa
{
public:void sit(){cout << "可以坐着" << endl;}void clean(){cout << "打扫沙发" << endl;}
};
class Bed
{
public:void lay(){cout << "可以躺着" << endl;}void clean(){cout << "打扫床" << endl;}
};// 派生
class SofaBed:public Sofa,public Bed
{
};int main()
{SofaBed sobe;sobe.lay();sobe.sit();sobe.Sofa::clean();sobe.Bed::clean();return 0;
}
2.菱形继承

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

有两种解决方式:
1、和重名问题似,使用基名::方式调用

2、使用虚继承
当出现虚继承时,Furniture中就会生成一张虚基表,这个表不占用任何对象的存储空间,属于Furniture持有,在程序启动时加载进内存空间,表中记录了Furniture函数的调用地址偏移量。
Bed和Sofa对象中会出现一个隐藏的成员变量指针,指向Furniture中的虚基表,占用对象的四个字节。
虚继承时,SofaBed对象会同时拥有两个虚基表指针成员,在调用时查表解决二义性。

#include <iostream>
using namespace std;// 家具厂
class Furniture
{
public:void func(){cout << "家具厂里有家具" << endl;}
};class Sofa:virtual public Furniture
{
};
class Bed:virtual public Furniture
{
};// 派生
class SofaBed:public Sofa,public Bed
{
};
int main()
{SofaBed sobe;sobe.func();return 0;
}

权限

1.权限修饰符

派生全局(外)
private√ ××
protected√ √ ×
public√ √ √ 
#include <iostream>
using namespace std;class Test
{
protected:string str="保护权限";
public:Test(){cout<<str<<endl;}
};class Base:public Test
{
public:Base(){cout<<str<<endl;}};int main()
{Base b;//cout<<b.str<<endl;// 错误 b是保护权限return 0;
}

2.不同权限的继承

(1)公有继承

        公有继承是使用最多的一种继承方式,在公有继承中,派生可以继承基的成员,但是不可以访问基的私有成员,基成员的权限在派生中权限保持不变。

#include <iostream>
using namespace std;class Base
{
private:string str1 = "私有成员";
protected:string str2 = "保护成员";
public:string str3 = "公有成员";
};class Son:public Base
{
public:Son(){//       cout << str1 << endl;      // 错误 str1为私有成员cout << str2 << endl;cout << str3 << endl;}
};int main()
{Son s1;return 0;
}

(2)保护继承

        在保护继承中,派生可以继承基的成员,不可以访问基的私有成员,基的公有成员与保护成员,在派生的权限都是保护权限。

#include <iostream>
using namespace std;class Base
{
private:string str1 = "私有成员";
protected:string str2 = "保护成员";
public:string str3 = "公有成员";
};
class Son:protected Base
{
public:Son(){//cout << str1 << endl;     // 错误 str1为私有成员cout << str2 << endl;cout << str3 << endl;}
};int main()
{Son s1;//    cout << s1.str3 << endl; // 保护成员调用失败return 0;
}

(3)私有继承

        在私有继承中,派生可以继承基的成员,但是不可以访问基的私有成员,基的公有成员与保护成员在派生中权限都是私有权限。

#include <iostream>
using namespace std;class Test
{
private:string str1="私有成员";
protected:string str2="保护成员";
public:string str3="公有成员";
};class Base:private Test
{
public:Base(){//        cout<<str1<<endl;cout<<str2<<endl;cout<<str3<<endl;}
};
class Demo:public Base
{
public:Demo(){cout << str2 << endl;cout << str3 << endl;}
};int main()
{Base b;
//    cout<<b.Test::str3<<endl;return 0;
}

http://www.ppmy.cn/news/1534112.html

相关文章

Flask-2

文章目录 请求全局钩子[hook]异常抛出和捕获异常abort 主动抛出HTTP异常errorhandler 捕获错误 context请求上下文(request context)应用上下文(application context)current_appg变量 两者区别&#xff1a; 终端脚本命令flask1.0的终端命令使用自定义终端命令 flask2.0的终端命…

python-斐波那契词序列/最大回文乘积/求最大最小k个元素

一:斐波那契词序列题目描述 编写一个程序&#xff0c;生成斐波那契词序列的前n个元素。 斐波那契词序列是一个词序列&#xff0c;其中每个词是通过连接前两个词形成的。 它以斐波那契序列命名&#xff0c;因为它是以类似的方式创建的&#xff0c;但是我们不是加数字&#xff0c…

基于STM32设计的智慧路灯(腾讯云IOT)(233)

文章目录 一、前言1.1 项目介绍【1】项目开发背景【2】设计实现的功能【3】项目硬件模块组成1.2 设计思路1.3 项目开发背景【1】选题的意义【2】可行性分析【3】参考文献【4】项目背景【5】摘要1.4 开发工具的选择【1】设备端开发【2】上位机开发1.5 系统功能总结1.6 系统框架图…

Vivado - JTAG to AXI Master (GPIO、IIC、HLS_IP)

目录 1. 简介 2. JTAG to AXI Master 2.1 添加 IP Core 2.2 基本TCL命令 2.2.1 复位 JTAG-to-AXI Master 2.2.2 创建并运行写入传输事务 2.2.3 创建并运行读取传输事务 2.2.4 命令列表 2.3 帮助信息 2.4 创建TCL读写程序 2.4.1 Read proc 2.4.2 Write proc 2.4.3 …

golang web笔记-2.请求request

什么是request http消息分为request&#xff08;请求&#xff09; 和 response&#xff08;响应&#xff09; request&#xff1a;在go中是一个struct&#xff0c;代表了客户段发送的http请求&#xff0c;已可以通过request 的方法访问请求中的cookie、URL、User Agent&#xf…

java日志门面之JCL和SLF4J

文章目录 前言一、JCL1、JCL简介2、快速入门3、 JCL原理 二、SLF4J1、SLF4J简介2、快速入门2.1、输出动态信息2.2、异常信息的处理 3、绑定日志的实现3.1、slf4j实现slf4j-simple和logback3.2、slf4j绑定适配器实现log4j3.2、Slf4j注解 4、桥接旧的日志框架4.1、log4j日志重构为…

四十一、多云/混合云架构设计(网络与基础设施管理)

3. 网络与基础设施管理 在多云和混合云架构中,网络与基础设施管理是确保跨云环境中应用和服务能够可靠、安全、高效运行的关键环节。网络设计的合理性、基础设施的可扩展性以及跨云平台的协调至关重要。以下是多云/混合云架构中网络与基础设施管理的核心原则、技术和常见的挑…

centos7.9使用docker容器方式部署jenkins环境

文章目录 前言一、版本信息二、环境准备2.1 切换源2.2 安装docker2.3 安装ntp2.4 关闭selinux2.5 安装docker-compose 三、jenkins安装3.1 镜像准备3.2 maven环境安装3.3 jdk环境安装3.4 nodejs环境安装3.5 yaml文件准备3.6 进行启动3.7 查看密码 总结 前言 记录在centos7.6安…