《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 …...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
