《C++ Primer》第7章 类(二)
参考资料:
- 《C++ Primer》第5版
- 《C++ Primer 习题集》第5版
7.4 类的作用域(P253)
每个类都有自己的作用域,在类的作用域之外,普通的数据和函数成员只能由对象、引用或指针使用成员访问运算符访问,类型成员则通过作用域运算符访问。
作用域和定义在类外部的成员
对于定义在类外部的函数成员而言,一旦遇到类名,定义的剩余部分就是在类的作用域之内了,这就是为什么参数列表和函数体可以直接使用类的其他成员:
void Window_mgr::clear(ScreenIndex i){ // ScreanIndex是类Window_mgr中的类型成员Screen &s = screens[i];s.contents = string(s.height*s.width, ' ');
}
需要注意的是,由于返回类型出现在类名之前,所以它位于类的作用域之外,所以如果我们的返回类型是 ScreenIndex
,就必须写成 Window_mgr::ScreenIndex
。
7.4.1 名字查找与类的作用域(P254)
一般的**名字查找(name lookup)**过程比较直接:
- 在名字所在块中寻找声明语句。
- 如果没找到,继续查找外层作用域。
- 如果最终没有找到,则报错。
这里名字查找只考虑出现名字使用之前出现的声明
由于类的定义分两步处理:首先编译成员的声明,然后编译函数体。所以成员函数体可以使用类中的任何名字,而无需考虑这个名字是否在前面出现过。
用于类成员声明的名字查找
需要注意的是,只有成员函数体具有特殊性,成员函数的参数列表和返回类型仍然遵循一般名字查找的规则:
using Money = double;
string bal;
class Account{
public:Money balance() { return bal; } // 这里bal的类型是Money,Money是double的别名
private:Money bal;
};
类型名要特殊处理
如果类成员使用了某个名字,这个名字代表某种类型,则类不能再重新定义这个名字。
using Money = double;
class Account{
public:Money balance() { return bal; }using Money = double; // 错误,Money不能重新定义,即使与原定义一致也不行
};
需要说明是,某些编译器会忽略这个约定(比如 visual studio )。
成员定义中的普通块作用域的名字查找
成员函数体内的名字按照如下方式查找:
- 在函数内查找该名字,只有在使用之前出现的声明才被考虑。
- 如果在函数内没找到,则在类内查找,此时无需考虑先后顺序。
- 如果类内没找到,则在成员函数定义之前的作用域内寻找。
// 最好不要隐藏同名成员,这里仅作演示
pos height;
void Screen::dummy_fcn(pos height){cursor = width * height; // 参数中的heightcursor = width * this->height; // 类中的heightcursor = width * Screen::height; // 类中的heightcursor = width * ::height; // 全局变量height
}
在文件中名字的出现处对其进行解析
class X{void func(int);
};
int y;
void X::func(int i){i = y; // y的声明已经出现过了,所以可以正常使用
}
7.5 构造函数再探(P257)
7.5.1 构造函数初始值列表(P258)
如果没有在构造函数初始值列表中显式初始化成员,则该成员将在函数体之前执行默认初始化:
Sales_data::Sales_data(const string &s, unsigned cnt, double price){bookNo = s;units_sold = cnt;revenue = cnt * price;
}
上面这段代码相当于先默认初始化成员,然后再给成员重新赋值。
构造函数的初始值有时必不可少
如果成员是 const
、引用或某种没有定义默认构造函数的类类型,则必须将其初始化。
一旦构造函数体开始执行,初始化就完成了。
成员初始化的顺序
在构造函数初始值列表中,每个成员只能出现一次。构造函数初始值列表只说明用于初始化成员的值,而没有限定初始化的顺序。成员的初始化顺与它们在类定义中的出现顺序一致:
class X {int i;int j;
public:// 想用未定义的j初始化i,再用val初始化jX(int val):j(val), i(j) { }
};
默认实参和构造函数
class Sales_data {
public:Sales_data(string s = "") :bookNo(s) { }
};
如果一个构造函数为所有参数都提供了默认实参,则它实质上也定义了默认构造函数。
7.5.2 委托构造函数(P261)
C++11 新标准允许我们使用委托构造函数(delegating constructor)。委托构造函数使用本类中其他构造函数执行自己的初始化过程:
class Sales_data {
public:Sales_data(string s, unsigned cnt, double price) :bookNo(s), units_sold(cnt), revenue(cnt *price) { }// 委托构造函数Sales_data() :Sales_data("", 0, 0) { }Sales_data(string s) :Sales_data(s, 0, 0) { }// 先委托默认构造函数,然后默认构造函数在委托三参数构造函数// 执行完受委托的构造函数后再执行委托函数的函数体Sales_data(istream &is) :Sales_data() { read(is, *this); }
};
7.5.3 默认构造函数的作用(P262)
当类类型的对象被默认初始化或值初始化时自动执行默认构造函数。
使用默认构造函数
Sales_data obj1(); // 声明了一个函数
Sales_data obj2; // 使用默认构造函数
7.5.4 隐式的类类型转换(P263)
如果某个类类型定义了能通过一个实参调用的构造函数,则它实际上定了转换成此类类型的隐式转换机制,这种构造函数称作转换构造函数(converting constructor):
class Sales_data {
public:// 转换构造函数Sales_data(string s) :bookNo(s), units_sold(0), revenue(0) { }
};
void func(Sales_data x) { ; }string null_book = "9-999-99999-9";
func(null_book); // 正确
可以理解为用这个参数隐式构造一个临时量。
只允许一步类类型转换
func("9-999-99999-9"); // 错误,需要先将const char*转换成string,再由string转换成Sales_data
类类型转换不是总有效
以上面的代码为例,并不是所有的 string
都是我们需要的 bookNo
。
抑制构造函数定义的隐式转换
我们可以将构造函数声明为 explicit
来阻止隐式类型转换:
class Sales_data {
public:// 阻止隐式类型转换explicit Sales_data(string s) :bookNo(s), units_sold(0), revenue(0) { }
};
void func(Sales_data x) { ; }string null_book = "9-999-99999-9";
func(null_book); // 错误
只能在类内声明构造函数时使用 explicit
,不能在类外使用 explicit
。
explicit
构造函数只能用于直接初始化
Sales_data item1(null_book); // 正确
Sales_data item2 = null_book; // 错误
为转换显式使用构造函数
func(static_cast<Sales_data>(null_book)); // 正确
7.5.5 聚合类(P266)
当一个类满足如下条件时,它是聚合类(aggregate class):
- 所有成员都是
public
。 - 没有定义任何构造函数。
- 没有类内初始值。
- 没有基类,也没有
virtual
函数。
聚合类允许用户直接访问其成员,并且具有特殊的初始化形式:
struct Data {int ival;string s;
};Data val1{ 0, "hello" };
如上面的代码所示,我们可以用一个花括号括起来的初始值列表来构造聚合类,初始值的顺序必须与类中定义的顺序相同。如果初始值列表中的元素个数少于类的成员数量,则靠后的成员被值初始化。
7.5.6 字面值常量类
数据成员都是字面值类型的聚合类是字面值常量类;如果一个类不是聚合类,但它符合下述要求,则它也是字面值常量类:
- 数据成员都是字面值类型。
- 类必须至少有一个
constexpr
构造函数。 - 如果一个数据成员有类内初始值,这个初始值或是一条常量表达式,或是调用数据成员自己的
constexpr
构造函数。 - 类必须使用析构函数的默认定义。
constexpr
构造函数
前面提到,字面值常量类至少有一个 constexpr
构造函数。构造函数可以声明成 = default
。否则, constexpr
必须既满足构造函数的要求(不需要 return
语句),又满足 constexpr
函数的要求(唯一可执行语句就是返回语句)。综合上述两点可知,constexpr
构造函数体一般为空:
class Debug {
public:constexpr Debug(bool b = true) :hw(b), io(b), other(b) { }constexpr Debug(bool h, bool i, bool o):hw(h), io(i), other(o) { }constexpr bool any() { return hw || io || other; }
private:bool hw;bool io;bool other;
};
constexpr
构造函数必须初始化所有数据成员(有默认初始值的成员可以不显式初始化),初始值或者使用constexpr
构造函数,或者是一条常量表达式。
7.6 类的静态成员(P268)
有时候,类需要一些成员与类本身直接相关,而不是与类的各个对象保持关联。
声明静态成员
我们可以在成员的声明前加上关键字 static
使之与类关联在一起:
class Account {
public:void calculate() { amount += amount * interestRate; }static double rate() { return interestRate; }static void rate(double);
private:string owner;double amount;static double interestRate;static double initRate();
};
类的静态成员存在于任何对象之外,所以每个 Account
对象将包含两个数据成员 owner
、amount
。interestRate
对象只有一个,并被所有对象共享。静态函数成员也不与任何对象绑定在一起,所以不包含 this
指针,也不能声明成 const
。
使用类的静态成员
可以使用作用域运算符直接访问静态成员:
double r;
r = Account::rate();
尽管静态成员不属于类的某个对象,但我们仍然可以通过类的对象、引用或指针来访问静态成员。
成员函数不通过作用域运算符也能直接使用静态成员:
class Account {
public:void calculate() { amount += amount * interestRate; }
private:static double interestRate;
}
定义静态成员
在类外部定义静态成员时,不能出现 static
关键字,static
只能出现在类内部的声明语句。
由于静态数据成员不属于类的任何一个对象,所以它们不是在创建类的对象时被定义的,也不是由构造函数初始化的。
一般来说,我们通常在任何函数外部定义和初始化静态成员:
double Account::interestRate = initRate();
由于见到 Account
后就知道当前处在类的作用域,所以后面调用 initRate()
不需要作用域运算符;同函数成员在类外部定义一样,interestRate
的定义也可以访问类中的私有成员。
静态成员的类内初始化
前面提到,类的静态成员不应该在类内初始化。但我们可以为静态成员提供 const
整数初始值(似乎没有这个要求?),但是要求静态成员必须是字面值常量类型 constexpr
且初始值必须是常量表达式。
class Account {
private:static constexpr int period = 30;double daily_tbl[period];
};
尽管一个常量静态数据成员已经在类内部初始化了,我们通常还是要在类外部定义一下这个成员,此时不能再指定初始值:
constexpr int Account::period;
静态成员能用于某些场景,而普通成员不能
静态数据成员可以是不完全类型,比如它本身所属的类类型:
class X{
public:static X mem1; // 正确X mem2; // 错误
};
此外,我们可以使用静态成员作为函数成员的默认实参。
相关文章:
《C++ Primer》第7章 类(二)
参考资料: 《C Primer》第5版《C Primer 习题集》第5版 7.4 类的作用域(P253) 每个类都有自己的作用域,在类的作用域之外,普通的数据和函数成员只能由对象、引用或指针使用成员访问运算符访问,类型成员则…...
git仓库代码克隆
说明: 由于服务项目的厂商不同且需求不断变化且交付周期临近,所以想把之前一个仓库的代码弄到一个新的仓库,待交付完毕后再进行代码整合。 第一步 先创建一个远程仓库用来放你要克隆的代码。 第二步 克隆一份裸版本代码库 git clone --ba…...

AM@向量代数@向量基本概念和向量线性运算
文章目录 abstract向量的基本概念向量向量的坐标分解式和坐标👺向量的模向量的长度(大小)👺零向量单位向量👺方向向量非零向量的单位向量正规化向量夹角👺 向量方向角和向量间夹角投影几何描述向量的线性运算向量的加减运算向量的…...
2023-11-08 LeetCode每日一题(最长平衡子字符串)
2023-11-08每日一题 一、题目编号 2609. 最长平衡子字符串二、题目链接 点击跳转到题目位置 三、题目描述 给你一个仅由 0 和 1 组成的二进制字符串 s 。 如果子字符串中 所有的 0 都在 1 之前 且其中 0 的数量等于 1 的数量,则认为 s 的这个子字符串是平衡子…...

Web3.0的测试题
任务: 在前端开发一个查询UI,查询当前用户账户的ETH余额和指定ERC20合约中的余额 目标: UI框架指定使用 MUI (https://mui.com)需要查询到当前账户的ETH余额并展示在UI界面上需要输入ERC20合约地址后,查询到到当前账户在此ERC20…...

Javascript知识点详解:对象的继承、原型对象、原型链
目录 对象的继承 原型对象概述 构造函数的缺点 prototype 属性的作用 原型链 constructor 属性 instanceof 运算符 构造函数的继承 多重继承 对象的继承 面向对象编程很重要的一个方面,就是对象的继承。A 对象通过继承 B 对象,就能直接拥有 B …...

学之思开源考试系统部署至Centos7
学之思开源考试系统部署至Centos7 1、下载源码 源码下载: https://gitee.com/mindskip/xzs-mysql 数据库脚本下载: https://www.mindskip.net:999/ 2、项目打包 分别在\source\vue\xzs-student目录和source\vue\xzs-admin目录,执行前端打…...
如何利用浏览器的可见性API优化网站性能
最近在使用微软AI聊天工具Bing时,发现一个有趣的东西。我向它提问后,它在持续输出的过程中,如果我离开了当前它的浏览器会话,比如切屏,看当前浏览器的其它标签页,它会默认停止它的输出,等我回来…...

还不知道IP地址不够用是怎么被大牛们解决的?(NAT/NAPT, IPv6, DHCP)
文章目录 前言1. DHCP网络管理协议什么是 DHCPDHCP 两种分配机制 2. NAT网络地址转换协议什么是 NATNAT 技术使用NAT网络设备间如何通信两个内网设备相互通信不同内网中的设备相互通信NAT IP转换过程 NAPT 技术NAT 技术的缺陷 3. IPv6 协议什么是 IPv6 总结 前言 在之前的文章…...

使用决策树预测隐形眼镜类型
任务描述 本关任务:编写一个例子讲解决策树如何预测患者需要佩戴的隐形眼镜类型。使用小数据集,我们就可以利用决策树学到很多知识:眼科医生是如何判断患者需要佩戴的镜片类型,一旦理解了决策树的工作原理,我们甚至也…...

[ACTF2020 新生赛]BackupFile 1
题目环境: 好好好,让找源文件是吧?咱们二话不说直接扫它后台 使用dirsearch工具扫描网站后台(博主有这个工具的压缩包,可以私聊我领取)python dirsearch.py -u http://0d418151-ebaf-4f26-86b2-5363ed16530…...
解决vuex刷新数据丢失
Vuex 是一个 Vue.js 的状态管理库,它使得你可以在 Vue 组件之间共享状态。当你在 Vuex 中更新状态时,如果你遇到数据丢失或数据不一致的问题,可能需要进行深度复制或者使用其他方式来确保数据的完整性。 假设你有一个 Vuex 存储,…...

linux系统下读取当前硬盘的温度
这个其实很简单,借助于smartctl工具(Ubuntu默认安装好了),标红的部分就是当前温度,单位是摄氏度。 sudo smartctl -l scttempsts /dev/sda...

python 深度学习 解决遇到的报错问题8
本篇继python 深度学习 解决遇到的报错问题7-CSDN博客 目录 一、OSError: [WinError 127] 找不到指定的程序。 Error loading "D:\my_ruanjian\conda-myenvs\deeplearning\lib\site-packages\torch\lib\caffe2_detectron_ops.dll" or one of its dependencies. 二、…...
Linux pipe()系统调用示例
Linux系统调用pipe函数,创建一个pipe,通过传入的fd数组返回pipe的读、写两端。 其中fd[ 0 ]用于读,fd[ 1 ]用于写。 一个pipe是单向数据传输的,不用用于父子进程双向读写。创建2个pipe实现父子进程间的双线读写。 #include <u…...
音频中的采样率和比特率
音频中的采样率和比特率 采样频率千比特率音频比特率 采样频率 参考:https://blog.csdn.net/qq_38907791/article/details/88925224 采样频率,也称为采样速度或者采样率,定义了每秒从连续信号中提取并组成离散信号的采样个数,它…...
Python常用脚本
1.解压指定文件夹内的zip包,解压到当前位置 import os import zipfile# 指定文件夹路径 folder_path "/path/to/your/folder"# 获取文件夹下所有的zip文件 zip_files [os.path.join(folder_path, file) for file in os.listdir(folder_path) if file.e…...

Redis5 分布式系统之主从模式
目录 分布式系统 引子 分布式系统类型 主从模式 一个主节点和多个从节点 创建多个节点方法 配置主从结构 主从模式知识 主从复制 拓扑结构 1.一主一从 2.一主多从 3.树形主从 主从实现原理 psync数据同步 全量复制和部分复制 psync流程 1.全量数据同步 2.部…...

【黑马程序员】Maven 进阶
文章目录 前言一、分模块开发与设计1. 分模块开发意义2. 分模块开发(模块拆分)2.1 创建 Maven 模块2.2 书写模块代码2.3 通过 Maven 指令安装模块到本地仓库(install 指令) 二、依赖管理1. 依赖传递1.1 依赖传递冲突问题 2. 可选依…...
231108 C语言memset当第三个参数为0,即设置个数为零也不报错
memset语法: void *memset(void *s, int c, size_t n); 犹豫第三个参数为0会不会报错,测试不会。 代码: #include"stdio.h" #include"stdlib.h" // memset memcpy int main() { int sig[100] { 0 }; int …...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...

免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...