C++ 类:类相关的非成员函数、构造函数

2023-11-19

前提: 仍有 Sales_data 类的代码:

struct Sales_data {
    std::string isbn() const { return bookNo; }		// 返回 isbn 编号
    Sales_data& combine (const Sales_data&);		// 模拟 += 运算
    double avg_price() const;
    std::string bookNo;
    unsigned units_sold = 0;						// 表示某书的销量
    double revenue = 0.0;							// 表示某书的总收入
};
// Sales_data 的非成员接口函数
Sales_data add(const Sales_data&, const Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);

定义类相关的非成员函数

从上代码可知,add, print, read 不属于类本身,从概念上讲 属于类接口的组成部分。

同样,通常把非成员函数的声明和定义分开。函数从概念是属于类,虽然不定义在类型中,也同样与类声明在同一个头文件中

一般来说,如果非成员函数是类接口的组成部分,则这些函数的声明应该与类放在同一个头文件中

定义 read 和 print 函数

简单说,read 和 print 函数作用就是类似于重载 >>, << 运算符

代码其实和直接输入也是非常相似:

istream &read(istream &is,Sales_data &item) {
    double price = 0;							// price 表示某书单价
    is >> item.bookNo >> item.units_sold >> price;
    item.revenue = price * item.units_sold;
    return is;
}

ostream &print(ostream &os,const Sales_data &item) {
    os << item.isbn() << " " << item.units_sold << " "
        << item.revenue << " " << item.avg_price();
    return os;
}
// 使用
int main() {
    Sales_data x;
    read(cin,x);
    print(cout,x);
    return 0;
}

关于 read 函数和 print 函数有两点重要的。

  • read 和 print 分别接受一个各自 IO 类型的引用作为参数,因为 IO 类不允许被拷贝;读取和写入都会改变流的内容,所以只能是普通引用,而非对常量的引用。返回类型是引用同理。

  • print 函数不负责换行,一般输出的函数应该尽量少对格式的控制,这样可以确保是否换行由用户决定。

定义 add 函数

看形参列表 以及 返回类型,可知调用为: c = add(a,b)。其中 a、b、c 都是 Sales_data 类型

Sales_data add(Sales_data &lhs,Sales_data &rhs) {
    Sales_data sum = lhs;
    sum.combine(rhs);
    return sum;
}

构造函数

​ 每个类都分别定义了它的对象被初始化的方式,类通过一个或者几个特殊的成员函数来控制其对象的初始化过程,这些函数叫做构造函数。构造函数的任务是初始化类对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数。

​ 构造函数的名字和类名相同。但是构造函数没有返回类型;除此以外和普通函数类似,构造函数有一个(可能为空)参数列表和一个函数体(可能为空)。类也可以包含多个构造函数,和重载函数差不多,必须在参数数量或者参数类型上有所区别。

​ 构造函数不能声明成 const,当创建一个 const 对象时,会直到构造函数完成初始化过程,对象真正的取得"常量"属性。因此,构造函数在 const 对象的构造过程中可以向其写值

合成的默认构造函数

​ 类通过一个特殊的构造函数来控制默认初始化过程,称为默认构造函数。默认构造函数无须任何实参。

​ 默认构造函数的其中一个特殊性是,如果类没有显示的定义构造函数,编译器会隐式的定义一个默认构造函数,称为合成的默认构造函数

合成的默认构造函数会按照以下规则初始化类的数据成员:

  • 如果存在类内的初始值,就用它初始化相应的成员
  • 否则,默认初始化该成员

注意:当类没有声明任何构造函数时,编译器才会自动生成默认构造函数

某些类不能依赖于合成的默认构造函数

​ 合成的默认构造函数只适合简单的类。一般来说,对于一个普通的类,都要定义自己的默认构造函数。

原因有三:

  • 当类没有声明任何构造函数时,编译器才会自动生成默认构造函数。除非自己再定义一个默认构造函数,否则只要我们定义了一个构造函数,该类就不会有默认构造函数。
  • 合成的默认构造函数可能会执行错误的操作。如果定义在块中的内置类型或复合类型(数组和指针等)执行默认初始化,则它们的值是未定义的。即用户创建类的对象时有可能得到未定义的值。(只有类中的内置类型或复合类型都有初始值时,才适合用合成的默认构造函数)
  • 类中类的时候。如果类中的类没有默认构造函数,编译器就无法初始化该成员。所以必须自定义默认构造函数。当然还有其他的情况也会导致这个问题。
定义 Sales_data 的构造函数

​ 先给出添加构造函数后的代码,后面会一一解释。

struct Sales_data {
    // 新增的构造函数
    Sales_data() = default;
    Sales_data(const std::string &s): bookNo(s) { }
    Sales_data(const std::string &s, unsigned n, double p):
    			bookNo(s), units_sold(n), revenue(p * n){ }
    Sales_data(std::istream &);
    // 之前已有的
    std::string isbn() const { return bookNo; }		// 返回 isbn 编号
    Sales_data& combine (const Sales_data&);		// 模拟 += 运算
    double avg_price() const;
    std::string bookNo;
    unsigned units_sold = 0;						// 表示某书的销量
    double revenue = 0.0;							// 表示某书的总收入
};

= default 的含义

​ Sales_data() = default;

可以明确一点:该构造函数不接受任何实参,所以是一个默认构造函数。定义这个函数的目的仅仅是因为需要其他形式的构造函数,也需要默认的构造函数。我们希望这个函数的作用完全等同于之前使用的 合成默认构造函数(!在这里有效的原因是 类的内置类型都提供了初始值,如果编译器不支持类内初始值,那么默认构造函数就应该使用构造函数初始值列表来初始化类的每个成员)。

​ C++11中,可以在参数列表后面写上 = default 来要求编译器生成构造函数。= default 可以声明出现在类的内部,也可以定义出现在类的外部。如果 = default 在类的内部,则默认构造函数是内联的;否则该成员默认情况下不是内联的。

构造函数初始值列表

Sales_data(const std::string &s): bookNo(s) { }
Sales_data(const std::string &s, unsigned n, double p):
    		bookNo(s), units_sold(n), revenue(p * n){ }

两个定义出现了新的部分,即冒号以及冒号和花括号之间的代码。

花括号定义了空函数体,因为构造函数的目的是为数据成员赋初值,一旦没有其他任务需要执行,函数体为空即可。

新出现部分叫做构造函数初始值列表,为相应的成员赋上括号内的初始值。

当某个数据成员被构造函数忽略时,它将以与合成认构造函数相同的方式隐式初始化。

建议:构造函数不应该轻易覆盖掉类内初始值,除非新赋的值和原值不同。

在类的外部定义构造函数

​ 与其他的几个不同,以 istream 为参数的构造函数需要一些实际的操作。

Sales_data::Sales_data(std::istream &is) {
    read(is, *this);
}

​ 这个函数没有返回类型,函数名和类名相同。显然是一个构造函数。尽管函数初始值列表是空的,但是执行了构造函数体,所以对象成员仍能被初始化。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 类:类相关的非成员函数、构造函数 的相关文章

  • C++基础——引用讲解1

    目录 6 1 引用概念 1 定义 2 引用的格式 2 引用的特性 三 引用的用法 1 有了引用 就不需要取地址了 2 引用的另一大用法 引用这么强的功能 能不能代替指针 6 1 引用概念 1 定义 引用不是新定义一个变量 而是给已存在的变量
  • python日志(2)——logging

    python日志 2 logging进阶 文章目录 python日志 2 logging进阶 一 logging的四大组件 1 logger 2 Handler 3 Formatter 二 输出日志的常用方式 总结 一 logging的四大
  • x86寄存器

    通用寄存器 GPR寄存器 AX Accumulator 累加寄存器 也叫累加器 BX Base 基地址寄存器 CX Count 计数器寄存器 DX Data 数据寄存器 指针寄存器 SP Stack Pointer 堆栈指针寄存器 BP B
  • 47 转置卷积 [动手学深度学习v2]

    转置卷积 卷积不会增大输入的高宽 通常要么不变 要么减半 转置卷积则可以用来增大输入高宽 Y i i
  • 测试自动化框架平台选型汇总篇

    本文介绍测试自动化常用的框架和平台 语言为python或javascript 无java UI自动化 python pytest playwright 文章 http t csdn cn 2dMF5 python selenium unit
  • 计算机英语·NO

    N n 换行符 name demanding 名字恢复 name mangling 名字毁坏 nanoseconds ns 纳秒 neg operations neg操作 negation two s complement 非 二进制补码的
  • 【电商】电商后台设计—订单中心

    订单系统连接了用户和商家 用户可以通过订单看到商品购买详情 商家则可以通过订单看到购买用户信息等 而整个订单系统囊括了许多模块 如订单生成 订单计算等 一 订单的生成与状态 1 下单过程 电商下单的过程相信大家都不陌生 我们以从购物车下单为
  • laravel路由

    路由 在laravel中 定义路由的地方在routes web php文件中 在使用laravel前必须先定义路由 然后才能在浏览器中访问 routes文件夹中还有一个api php 用于定义api路径 最简单的路由 Route get f
  • GPIO_Speed_50MHz的值是多少

    在STM32底层库中 使用GPIO Speed 50MHz等来表示输出口的最高频率 那么他的值是多少 查看了一下定义 是个枚举 后来查了一下枚举才知道 当枚举中的某个元素备赋值后 从该元素往后是递增的 即1 2 3 当赋给指定的偏移地址后
  • react中props详解

    1 props的基本使用 react组件之间的传值 是离不开props的 代码展示 export default class Parent extends Component render return div h3 我是Parent组件
  • python中if else语句用法_Python条件语句详解:if、else、switch都有了

    01 if条件语句 if语句用于检测某个条件是否成立 如果成立 则执行if语句内的程序 否则 跳过if语句 执行后面的内容 if语句的格式如下 if 表达式 语句1 else 语句2 if语句的执行过程如下 如果表达式的布尔值为真 则执行语
  • PyAutoGui图像操作(一):图像定位方式及返回

    一 PyAutoGui介绍 PyAutoGUI是Python功能强大的UI自动化库 其目的是可以用程序自动控制鼠标和键盘操作 主要用来实现PC端的UI自动化 有鼠标控制 键盘操作 屏幕截图 图片定位 消息对话框 窗口操作等功能 有倒计时 鼠
  • 使用IntelliJ IDEA开发Java Web HelloWorld

    下载Tomcat 首先 下载Apache Tomcat并解压到本地计算机 可存放于任何位置 tomcat官网 https tomcat apache org 下载稳定版版本即可 下载解压好 启动bin下的startup bat文件 访问 h
  • 图神经网络调研

    图神经网络调研 图神经网络简介 应用场景 典型模型 GCN PinSAGE GraphSAGE GAT 图神经网络与知识图谱 异质图和同质图 部分参考链接 最近在做一些图神经网络相关的课题 做了些调研 简单记录一下想法和收获 图神经网络简介
  • OSFormer阅读笔记

    目录 前言 1 模型的特点 2 模型结构 2 1 CNN backbone 2 2 位置感知Transformer LST 2 3 粗细特征融合 CFF 2 4 动态伪装实例归一化 DCIN 2 5 损失函数 3 可视化分析 参考 前言 O
  • JAVA面向过程(二十六) if-else if-else多选择结构

    语法结构 1 2 3 4 5 6 7 8 9 10 if 布尔表达式1 语句块1 else if 布尔表达式2 语句块2 else if 布尔表达式n 语句块n else 语句块n 1 当布尔表达式1为真时 执行语句块1 否则 判断布尔表达
  • 微信小程序反编译

    安装逍遥安卓模拟器 安装node js不作细述 按提示安装即可 安装完成后在命令窗口输入 node v 能查看到版本号即可 打开脚本地址下载反编译脚本 解压 安装依赖 在脚本目录下shift 鼠标右键 打开命令窗口 逐条命令安装 npm i
  • python requests请求终止,python中requests小技巧

    关于 Python requests 在使用中 总结了一些小技巧把 记录下 1 保持请求之间的Cookies 我们可以这样做 2 请求时 会加上headers 一般我们会写成这样 唯一不便的是之后的代码每次都需要这么写 代码显得臃肿 所以我

随机推荐

  • opencv+mfc应用程序依赖库

    msvcp110 dll msvcr110 dll vccorlib110 dll和它统计目录下的所有库 https blog csdn net seu nuaa zc article details 53525569 ucrtbased
  • PC端:hover 样式在移动端的兼容处理

    写了一个轮播 有hover效果 PC端正常显示 移动端就出现一些问题 点击之后滑动到下一张 上一张有hover效果 要点击其他地方才会消失 解决方案 参考文章CSS hover 样式在移动端的兼容处理 链滴 media hover hove
  • mysql唯一索引如何实现,从基础到源码统统帮你搞定

    面试失败之后 在这之后的很长一段时间后 他没敢再去面试 觉得自己太丢人 技术太差不敢去谈论技术话题 后来他渐渐开始看网上的文章 知道要刷leetcode 看面经 然后之后一年多的时间 边上课 边刷题 边看知识点 直到去年中旬 第二次开始面试
  • ImportError: cannot import name ‘soft_unicode‘ from ‘markupsafe‘

    打开jupyter notebook报下面的错误 这里说是markupsafe有错误 查看一下是否安装了这个包 pip show markupsafe 已经有了这个包 但是还报错 经过测试发现这个2 1 3版本不兼容 回退到2 1 0才行
  • 一文搞定Nginx的压缩、黑白名单、防盗链、零拷贝、跨域、双机热备等知识

    点击上方 Java基基 选择 设为星标 做积极的人 而不是积极废人 每天 14 00 更新文章 每天掉亿点点头发 源码精品专栏 原创 Java 2021 超神之路 很肝 中文详细注释的开源项目 RPC 框架 Dubbo 源码解析 网络应用框
  • C++操作SQLite数据库

    准备工作 在使用C 操作SQLite之前 需要获得sqlite3 h sqlite3 lib sqlite3 dll 大家可以在 这里 下载 并将这3个文件导入VC 工程中 其中sqlite3 dll文件放到Debug文件夹里 SQLite
  • 01一刷AD20入门教程记录

    学习视频 Altium Designer 20 19 入门到精通全38集 四层板智能车PCB设计视频教程AD19 AD20 凡亿 哔哩哔哩 bilibili 工程 安装现有元件库和封装库 AD20 1 打开优选项设置 gt Data Man
  • 分享一个多类别超实用AI工具集合网站,你想要的AI网站这里全都有

    随着Al技术的快速发展 人工智能也变成当今热门的领域之一 它正在改变着我们的生活和工作方式 今天就和大家分享一个多类别超实用AI工具集合网站 你想要的AI网站这里全都有 全网AI工具一城选择 AI 城市是一个集合了各种人工智能相关的工具和资
  • 如何判断服务器在国内还是海外?

    已知晓网站或是已知晓IP 如何判断该服务器是国内的还是海外的呢 在已知网站域名的情况下 可以通过ping网站域名来获得服务器的IP 当然有些网站是做了禁ping的 不知道IP的情况下是无法判断是国内还是海外的机器 比如咱们已知晓IP是103
  • Java响应式编程

    Java响应式编程 一 lambda表达式 1 1 lambda简介 1 2 lambda语法 1 3 FunctionalInterface 1 4 方法引用 1 5 类型推断 1 6 变量引用 1 7 级联表达式和柯里化 二 Strea
  • RFID桌面一体机在珠宝管理中的应用

    1 产品 铨顺宏 FU M6 USB N是一款以Thingamgic优异的M6e NANO模组为核心集成桌面式RFID一体机 RFID桌面工作台外观轻薄时尚 便于安装同时不影响美观 适用于进行RFID标签的读取 写入等绑定操作 同时 设备具
  • 【华为OD统一考试B卷

    华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一修改为OD统一考试 A卷 和OD统一考试 B卷 你收到的链接上面会标注A卷还是B卷 请注意 根据反馈 目前大部分收到的都是
  • oracle清除试图的数据,ORACLE大表中删除部分数据最佳方案

    如果业务无法停止的话 主要有这三种思路 思路1 根据ROWID分片 再利用Rowid排序 批量处理 回表删除 在业务无法停止的时候 选择这种方式 的确是最好的 一般可以控制在每一万行以内提交一次 不会对回滚段造成太大压力 我在做大DML时
  • Python+Selenium安装及环境配置

    转自 https www cnblogs com sandysun p 7838113 html 一 Python安装 Window系统下 python的安装很简单 访问python org download 下载最新版本 安装过程与其他w
  • 摄像头网络模组的使用

    摄像头和网络模组尾线如上图所示 利用上面两个模组 打算自己做一个简单的网络摄像头 在上面的模组网站上找到对应模组的接口定义资料 因为我们要自己将对应的网线接口等接好 本模组的接口如下 具体的该模组的接线如上所示 该网络模组尾线总共有 根 根
  • B07_NumPy 高级索引(整数数组索引,布尔索引,花式索引)

    NumPy高级索引 NumPy 比一般的 Python 序列提供更多的索引方式 除了之前看到的用整数和切片的索引外 数组可以由整数数组索引 布尔索引及花式索引 整数数组索引 以下实例获取数组中 0 0 1 1 和 2 0 位置处的元素 实例
  • 文储研习社第17期

    文储研习社是文储区块链技术人员自发组织的学习交流社区 旨在于追踪区块链时下最新热点 解码热点蕴含的未知领域 享受思想交流的碰撞 欢迎志同道合的小伙伴加入我们 共同学习与成长 第17期 为了提高考证通过率 不小心搭了条链 作者 Bingo 你
  • java springboot 8080端口号冲突时 修改当前项目端口号

    背景 springboot 项目启动时报错 Web server failed to start Port 8080 was already in use 报错原因 端口被占用 解决方案 修改项目application properties
  • SpringCloud整合Eureka出现“Error creating bean with name ‘configurationPropertiesBeans‘ defined in......“

    笔者在实现SpringCloud整合Eureka注册中心时出现如下报错 org springframework beans factory BeanCreationException Error creating bean with nam
  • C++ 类:类相关的非成员函数、构造函数

    前提 仍有 Sales data 类的代码 struct Sales data std string isbn const return bookNo 返回 isbn 编号 Sales data combine const Sales da