C++(第四天----拷贝函数、类的组合、类的继承)
一、拷贝构造函数(复制构造函数)
1、概念
拷贝构造函数,它只有一个参数,参数类型是本类的引用。如果类的设计者不写拷贝构造函数,编译器就会自动生成拷贝构造函数。大多数情况下,其作用是实现从源对象到目标对象逐个字节的复制,让目标对象的每个成员变量都变得和源对象相等。编译器自动生成的拷贝构造函数称为“默认拷贝构造函数”。
2、格式
class 类名{
public://普通构造函数类名(){ } //拷贝构造函数 ,参数是本类的引用//如果我们没有自定义,那么编译器就自动生成默认的拷贝构造函数类名(const 类名 &a){ }
};
3、例子
#include<iostream>
#include<cstring>using namespace std;class Student{public:Student(const char *name,int age);//普通构造函数~Student();//如果没有定义拷贝构造函数,编译器就自动生成默认拷贝构造函数--浅拷贝---值的拷贝/* Student(Student &a){this->name = a->name;this->age = age;} *///自定义的拷贝构造函数,深拷贝Student(Student &a){cout<<"Student(Student &a)"<<endl;//分配内存空间this->name = new char[256];strcpy(this->name,name);this->age = age;}
private:char *name;int age;
};Student::Student(const char *name,int age=20) //name 记录的是 数据段.rodata 中的"张3" 内存空间起始地址
{cout<<"Student(char *name,int age=20)"<<endl;//分配内存空间this->name = new char[256];strcpy(this->name,name);this->age = age;
}Student::~Student()
{cout<<"~Student()"<<endl;delete []this->name;
}int main()
{//根据参数调用普通构造函数Student mya("张3");//如果是在定义一个对象的时候通过另一个对象来初始化,那么会调用拷贝构造函数//Student myb(mya);Student myb = mya;//打印内存空间的地址cout<<"mya.name:"<<static_cast<const void *>(mya.name)<<endl;cout<<"myb.name:"<<static_cast<const void *>(myb.name)<<endl;return 0;
}
4、什么时候需要自己定义拷贝构造函数
当类的数据成员中 有指针成员的时候,需要申请内存空间
5、什么时候会调用到拷贝构造函数
在定义一个对象通过另一个对象来初始化,那么会调用拷贝构造函数
6、深拷贝 和 浅拷贝
浅拷贝:只拷贝对象本身空间里面的内容
深拷贝:拷贝对象本身空间内容的同时,还要分配成员指向的堆空间并且进行拷贝
二、类的组合
1、概念
一个类的对象作为另外一个类的数据成员。
也就是说,两个及以上相互独立的类能够放在一起,然后通过一个类就可以调用另一个类的对象从而调用另一个类的功能。
2、案例
一台电脑computer和一台打印机DaYinJi两个独立的类,computer有显示、算数,DaYinJi有打印功能,当组合在一起后,对于computer类就可以调用打印机的打印功能。
#include <iostream>using namespace std;//打印机类
class DaYinJi{
public:DaYinJi(){cout<<"DaYinJi()"<<endl;}~DaYinJi(){cout<<"~DaYinJi()"<<endl;}DaYinJi(int data):a(data){cout<<"DaYinJi(int data)"<<endl;}int a;void DaYin(){cout<<"正在打印....."<<endl;}
};class Demo{
public:Demo(int c){cout<<"Demo()"<<endl;}~Demo(){cout<<"~Demo()"<<endl;}
};//类的组合
/*
需要解决的问题??
1、在类的组合中,如何指定 内嵌对象的构造函数??? 在本类的构造函数的初始化列表上指定内嵌对象的构造函数
2、构造函数的执行顺序 是 打印机类的构造函数 再到 电脑类的构造函数
*///电脑类
class Computer{public://在本类的构造函数的初始化列表上指定内嵌对象的构造函数Computer():e(2000),d(1000){cout<<"Computer()"<<endl;}~Computer(){cout<<"~Computer()"<<endl;}void exec(){d.DaYin();}
private://私有成员 是 另一个类的对象 --如何去指定 对象的构造函数???//内嵌对象//如果有多个内嵌对象,那么多个内嵌对象的 构造函数的执行顺序跟 定义的先后顺序有关 跟 构造函数指定的先后顺序 无关DaYinJi d;Demo e;
};int main()
{//实例化一个电脑类的对象Computer c1;//我要用电脑打印文件c1.exec();return 0;
}
3、构造函数调用顺序
在构造函数的列表中指定内嵌对象的构造函数的形式
如果不指定内嵌对象构造函数的形式,则调用的默认构造函数
顺序:内嵌对象 ==》本类
多个内嵌对象 顺序:按照定义内嵌对象的先后顺序调用内嵌对象的构造函数
【注意】不能在构造函数的初始化列表中初始化内嵌对象成员
4、析构函数调用顺序
顺序:本类==》内嵌对象
练习1:设计一个圆形类(Circle),和一个点类(Point),计算点和圆的关系(判断该点是在圆内、圆外还是圆上)。
#include <iostream>using namespace std;//点类
class Point
{
public://设置X坐标void setX(int x) {m_x = x;}//获取X坐标int getX() {return m_x;}//设置Y坐标void setY(int y){m_y = y;}//获取Y坐标int getY(){return m_y;}
private:int m_x;int m_y;
};
//设置一个圆类Circle
class Circle
{
public://设置半径void setR(int r){m_R = r;}//获取半径int getR(){return m_R;}//设置圆心void setCenter(Point center) {m_center = center;}//获取圆心Point getCenter() //m_center是Piont类的数据{return m_center;}
private:int m_R;//在类中可以让另一个类 作为本类中的成员--与结构体相似Point m_center;
};//判断点和圆的关系
void isInCircle(Circle &c, Point &p)
{if ((p.getX() - c.getCenter().getX()) * (p.getX() - c.getCenter().getX()) + (p.getY() - c.getCenter().getY()) * (p.getY() - c.getCenter().getY()) == c.getR() * c.getR())cout << "点在圆上" << endl;else if ((p.getX() - c.getCenter().getX()) * (p.getX() - c.getCenter().getX()) + (p.getY() - c.getCenter().getY()) * (p.getY() - c.getCenter().getY()) > c.getR() * c.getR())cout << "点在圆外" << endl;else cout << "点在圆内" << endl;
}int main()
{//创建并设置点P1Point P1;P1.setX(10);P1.setY(9);//创建并设置点P2--圆心Point P2;P2.setX(10);P2.setY(0);//设置圆C1Circle C1;C1.setR(10);C1.setCenter(P2);isInCircle(C1, P1);system("pause");return 0;
}
三、类的继承
1、概念
新的类(子类)从已知的类(父类)中得到已有的特征的过程
新类叫派生类/子类
已知的类叫基类/父类
2、作用
继承可以减少重复的代码。比如父类已经提供的方法,子类可以直接使用,不必再去实现。
class 子类名:继承方式 父类(继承方式省略默认是私有继承)
{子类成员
};
4、例子
#include<iostream>using namespace std;//父类/基类
class Base{public: Base(){cout<<"Base()"<<endl;}~Base(){cout<<"~Base()"<<endl;}void setValue(int value){base_a = value;}int getValue(){return base_a;}void showValue(){cout<<"base_a:"<<base_a<<endl;}int base_a;
};class Child:public Base{public:Child(){cout<<"Child()"<<endl;}~Child(){cout<<"~Child()"<<endl;}
};int main()
{Child mya;//直接调用基类的函数成员mya.setValue(100);mya.showValue();return 0;
}
5、什么情况下用继承
基类和派生类之间存在is-a关系
6、总结
- 在派生类的构造函数的初始化列表中指定基类的构造函数
- 构造函数调用顺序:基类–》派生类
- 析构函数调用顺序:派生类–》基类
练习2:设计一个人的类(基类),再分别设计一个学生类(子类) 和 教师类 (子类)单继承 人类
人类:
属性:姓名、年龄、性别
方法:吃饭、睡觉
学生类:
属性:姓名、年龄、性别、分数、学号
方法:吃饭、睡觉、打游戏、学习
教师类:
属性:姓名、年龄、性别、教龄、工作类别(教的是语文还是数学还是英语)
方法:吃饭、睡觉、学习、备课
四、继承方式
继承方式有三种: 公有继承(public)、保护继承(protected)、私有继承(private)
上面表格权限是基类中的成员继承到子类后的成员权限
-
如果派生类在继承基类的时候选择 公有继承(public)
那么基类的公有成员就是在派生类中也是充当公有成员,可以直接在派生类的内部和外部使用
那么基类的保护成员就是在派生类中也是充当保护成员,可以在派生类的内部使用,但是不能外部使用
那么 基类的私有成员可以继承,但是不能直接访问,可以通过基类的公有方法和保护方法间接访问 -
如果派生类在继承基类的时候选择 保护继承(protected)
那么基类的公有成员就是在派生类中是充当保护成员,可以在派生类的内部使用,但是不能外部使用
那么基类的保护成员就是在派生类中是充当保护成员,可以在派生类的内部使用,但是不能外部使用
那么 基类的私有成员可以继承,但是不能直接访问,可以通过基类的公有方法和保护方法间接访问 -
如果派生类在继承基类的时候选择 私有继承(private)
那么基类的公有成员就是在派生类中是充当私有成员,可以在派生类的内部使用,但是不能外部使用
那么基类的保护成员就是在派生类中是充当私有成员,可以在派生类的内部使用,但是不能外部使用
那么 基类的私有成员可以继承,但是不能直接访问,可以通过基类的公有方法和保护方法间接访问
#include<iostream>using namespace std;//基类
class Base{public:Base(int data=100):baseData(data){cout<<"Base()"<<endl;}~Base(){cout<<"~Base()"<<endl;}void setData(int data){baseData = data;}int getData(){return baseData;}
protected:void showData(){cout<<"baseData:"<<baseData<<endl;}
private:int baseData;
};class Child:private Base{public:Child(int data=20):Base(data){showData();cout<<"Child()"<<endl;}~Child(){cout<<"~Child()"<<endl;}
};
int main()
{Child mya(200);//mya.setData(1000);return 0;
}
四、总结:
类的组合(has-a)和类的继承(is-a)是面向对象编程中两种重要的关系,它们在概念、使用场景、实现方式等方面存在显著的区别。以下是这两者的详细对比:
一、概念区别
类的组合(has-a):
指的是一个类(或对象)包含另一个类的对象作为其属性。这种关系强调的是“拥有”或“包含”的关系。
例如,一个汽车类可以包含一个发动机类的对象作为属性,表示汽车拥有发动机。
类的继承(is-a):
指的是一个类(子类)继承另一个类(父类)的属性和方法。这种关系强调的是“是一种”的关系。
例如,一个猫类可以继承一个动物类,表示猫是动物的一种。
二、使用场景区别
类的组合:
当一个类需要复用另一个类的功能,但又不希望与其产生过于紧密的联系时,使用组合。
组合关系有助于降低类之间的耦合度,提高系统的灵活性和可维护性。
例如,在设计一个图形界面时,一个窗口类可以包含多个按钮类的对象,而不需要通过继承来实现。
类的继承:
当一个类需要继承另一个类的属性和方法,并且这些属性和方法对于子类来说具有普遍意义时,使用继承。
继承关系有助于实现代码的重用和扩展,但也可能导致类之间的耦合度过高,增加系统的复杂性。
例如,在设计一个动物类库时,可以使用继承来定义不同种类的动物,如猫、狗等,它们都继承自动物类。
三、实现方式区别
类的组合:
在组合类中,通过属性来持有另一个类的对象。
可以通过这个属性来调用被包含类的方法,但不需要继承其所有方法和属性。
组合关系通常是在运行期确定的,因为可以在运行时动态地创建和销毁被包含类的对象。
类的继承:
在继承关系中,子类继承父类的所有非私有属性和方法(在Java等语言中,私有属性和方法不能被继承)。
子类可以覆盖(Override)父类的方法,以提供自己的实现。
继承关系在编译期就已经确定,因为子类需要知道父类的所有接口才能正确地实现它们。
四、其他区别
耦合度:组合关系是一种松耦合关系,而继承关系则是一种紧耦合关系。松耦合关系有助于降低系统各部分之间的依赖程度,提高系统的灵活性和可维护性。
多态性:在继承关系中,可以实现类型的回溯(即子类对象可以被当作父类对象来使用),从而实现多态性。而在组合关系中,通常不具备这种特性。
设计原则:在面向对象设计中,通常建议优先考虑组合而不是继承。因为组合关系更加灵活,可以更好地应对变化。而继承关系则可能导致系统结构过于复杂和僵化。
综上所述,类的组合和类的继承在面向对象编程中各有其独特的优势和适用场景。在实际应用中,应根据具体需求和设计原则来选择合适的关系来实现代码的组织和复用。
相关文章:

C++(第四天----拷贝函数、类的组合、类的继承)
一、拷贝构造函数(复制构造函数) 1、概念 拷贝构造函数,它只有一个参数,参数类型是本类的引用。如果类的设计者不写拷贝构造函数,编译器就会自动生成拷贝构造函数。大多数情况下,其作用是实现从源对象到目…...

第一课:接口配置IP地址:DHCP模式
希望pc1,pc2,pc3自动分配到ip地址。 实验拓扑: 配置:高级一点的路由器还是手动配置: [R1]int g0/0/0 [R1-g0/0/0]ip address 192.168.1.1 255.255.255.0 打开PC1,切换到DHCP模式,点击应用,再到命令行输入ipconfig&…...
esp32_spfiffs
生成 spiffs image python spiffsgen.py <image_size> <base_dir> <output_file> eg, python spiffsgen.py 0x2000 ./folder hello.bin Arduino 的库有例子可以直接用于 OTA 升级 spiffs 分区 HTTPUpdateResult HTTPUpdate::updateSpiffs(HTTPClient &h…...

每日一练全新考试模式解锁|考试升级
🙋频繁有小伙伴咨询:我想举办一场历时一个月的答题活动,学生可以每天打开答题,活动完结后可以导出每天的答题成绩 此前我们都会让小伙伴创建30场考试,然后使用批量分享功能组合起来,对外分享一个链接就可以…...
pyqt5图片分辨率导致的界面过大的问题
项目场景:pyqt5的图片分辨率和屏幕分辨率问题 提示:这里简述项目相关背景:图片分辨率:500*500;显示屏分辨率:600;导致界面显示不全; 在其他高分辨率显示屏中没有这个问题。 问题描述…...

(三)前端javascript中的数据结构之集合
集合的特点 1.无序 2.唯一性 3.不可重复 集合相对于前面几种数据结构,比较简单好理解,看看代码实现就能知道他的用法了 集合的创建 function MySet() {this.item {}; } MySet.prototype.has function (value) {return value in this.item; };//增 M…...

VuePress 的更多配置
现在,读者应该对 VuePress、主题和插件等有了基本的认识,除了插件,VuePress 自身也有很多有用的配置,这里简单说明下。 VuePress 的介绍 在介绍了 VuePress 的基本使用、主题和插件的概念之后,我们再来看看官…...
问题解决|Python 代码的组织形式与编码规范
一、Python中组织形式 (一)组织形式(函数,类,模块,包,库)概览 (1)概览 组织形式描述特点或用途例子函数一段具有特定功能的代码块,可以接受参数…...

Flask项目搭建及部署 —— Python
flask搭建及部署 pip 19.2.3 python 3.7.5 Flask 1.1.1 Flask-SQLAlchemy 2.4.1 Pika 1.1.0 Redis 3.3.11 flask-wtf 0.14.2 1、创建flask项目: 创建完成后整个项目结构树: app.py: 项⽬管理⽂件,通过它管理项⽬。 static: 存放静态…...

【C++报错已解决】Invalid Use of ‘this’ Pointer
🎬 鸽芷咕:个人主页 🔥 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 文章目录 引言 一、问题描述1.1 报错示例1.2 报错分析1.3 解决思路 二、解决方法2.1 方法一:修正‘this’指针使用2…...

群晖NAS配置WebDav服务结合内网穿透实现跨平台云同步思源笔记
文章目录 前言1. 开启群晖WebDav 服务2. 本地局域网IP同步测试3. 群晖安装Cpolar4. 配置远程同步地址5. 笔记远程同步测试6. 固定公网地址7. 配置固定远程同步地址 前言 本教程主要分享如何将思源笔记、cpolar内网穿透和群晖WebDav三者相结合,实现思源笔记的云同步…...

内容监管与自由表达:Facebook的平衡之道
在当今数字化信息社会中,社交媒体平台不仅是人们交流和获取信息的主要渠道,也是自由表达的重要舞台。Facebook,作为全球最大的社交网络平台,连接了数十亿用户,形成了一个丰富多样的信息生态。然而,如何在维…...

电脑桌面日历记事本怎么弄 好用的桌面日历记事本
在这个数字化的时代,电脑已成为我们日常生活中不可或缺的伙伴。我常常在电脑上记录各种事项,以便随时查看和提醒自己。而我最钟爱的记事方式,莫过于使用桌面日历记事本。 想象一下,你的电脑桌面上有一个直观的日历,每…...

#招聘数据分析#2024年6月前程无忧招聘北上广深成渝对比情况
#招聘数据分析#2024年6月前程无忧招聘北上广深成渝对比情况 0、根据前程无忧不完全样本统计,北上广深成都重庆平均月工资从高到低依次为 北京15441元、上海14425元、深圳13310元、广州11192元、成都10539元、重庆10290。 1、成都招聘样本数全量35228个,…...

STM32 IIC详解(软件模拟)
目录 一、IIC协议基本原理 1.IIC协议概述 2.时序图分析 二、代码分析 1.IIC初始化 2.IIC起始信号 3.IIC发送数据 4.获取应答信号 5.读一个字节 6.产生ACK应答 7.不产生ACK应答 IIC(Inter-Integrated Circuit)在嵌入式系统中是一种常见的数据通…...
推三返一小程序商城开发搭建
推三返一小程序商城开发主要涉及到以下几个步骤: 确定需求和功能: 首先需要明确小程序商城的开发需求和功能。商家需要思考自己想要实现什么功能,例如推广、销售、裂变等。 同时还需要考虑小程序的页面设计、用户体验等方面。 选择合适的开…...

项目机会:4万平:智能仓,AGV,穿梭车,AMR,WMS,提升机,机器人……
导语 大家好,我是社长,老K。专注分享智能制造和智能仓储物流等内容。 如下为近期国内智能仓储物流相关项目的公开信息线索,这些项目具体信息会发布到知识星球,请感兴趣的球友先人一步到知识星球【智能仓储物流技术研习社】自行下载…...

[工具类]Java 合并、拆分PPT幻灯片
本文将介绍在Java程序中如何来合并及拆分PPT文档的方法。示例大纲: 1. 合并 1.1 将指定幻灯片合并到文档 1.2 合并多个幻灯片文档为一个文档 2. 拆分 2.1 按幻灯片每一页单独拆分为一个文档 2.2 按指定幻灯片页数范围来拆分为多个文档 使用工具:F…...
深入了解 Timber:全面掌握 Android 日志记录的最佳实践
深入了解 Timber:全面掌握 Android 日志记录的最佳实践 Timber 是由 Jake Wharton 提供的一个流行的 Android 日志记录库。它旨在简化日志记录、增强日志管理,并提高代码的可维护性。在本文中,我们将深入探讨 Timber 的功能、使用方法以及最…...

阿一课代表随堂分享:红队反向代理之使用frp搭建反向代理
frp反向代理 frp简介 frp 是一个开源、简洁易用、高性能的内网穿透和反向代理软件,支持 tcp, udp, http, https等协议。 frp 是一个可用于内网穿透的高性能的反向代理应用,分为服务端frps和客户端frpc,支持 tcp, udp, http, https 协议。详…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...

Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一:HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二:Floyd 快慢指针法(…...
字符串哈希+KMP
P10468 兔子与兔子 #include<bits/stdc.h> using namespace std; typedef unsigned long long ull; const int N 1000010; ull a[N], pw[N]; int n; ull gethash(int l, int r){return a[r] - a[l - 1] * pw[r - l 1]; } signed main(){ios::sync_with_stdio(false), …...

数据分析六部曲?
引言 上一章我们说到了数据分析六部曲,何谓六部曲呢? 其实啊,数据分析没那么难,只要掌握了下面这六个步骤,也就是数据分析六部曲,就算你是个啥都不懂的小白,也能慢慢上手做数据分析啦。 第一…...