《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)**过程比较直接:

  1. 在名字所在块中寻找声明语句。
  2. 如果没找到,继续查找外层作用域。
  3. 如果最终没有找到,则报错。

这里名字查找只考虑出现名字使用之前出现的声明

由于类的定义分两步处理:首先编译成员的声明,然后编译函数体。所以成员函数体可以使用类中的任何名字,而无需考虑这个名字是否在前面出现过。

用于类成员声明的名字查找

需要注意的是,只有成员函数体具有特殊性,成员函数的参数列表和返回类型仍然遵循一般名字查找的规则:

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 )。

成员定义中的普通块作用域的名字查找

成员函数体内的名字按照如下方式查找:

  1. 在函数内查找该名字,只有在使用之前出现的声明才被考虑。
  2. 如果在函数内没找到,则在类内查找,此时无需考虑先后顺序。
  3. 如果类内没找到,则在成员函数定义之前的作用域内寻找。
// 最好不要隐藏同名成员,这里仅作演示
pos height;
void Screen::dummy_fcn(pos height){
    cursor = width * height;    // 参数中的height
    cursor = width * this->height;    // 类中的height
    cursor = width * Screen::height;    // 类中的height
    cursor = 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初始化j
    X(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 对象将包含两个数据成员 owneramountinterestRate 对象只有一个,并被所有对象共享。静态函数成员也不与任何对象绑定在一起,所以不包含 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;    // 错误
};

此外,我们可以使用静态成员作为函数成员的默认实参

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/120897.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【公益案例展】火山引擎公益电子票据服务——连接善意,共创美好

‍ 火山引擎公益案例 本项目案例由火山引擎投递并参与数据猿与上海大数据联盟联合推出的 #榜样的力量# 《2023中国数据智能产业最具社会责任感企业》榜单/奖项”评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 捐赠票据是慈善组织接受捐赠后给捐赠方开具的重要凭证&…

saleae逻辑分析仪在win10上的安装: 驱动安装失败的解决办法

1. 安装 安装64位的&#xff1a;Logic Setup 1.1.16 (64-bit).exe 选择安装目录&#xff1a; 安装其间&#xff0c;如果弹出驱动安装对话框&#xff0c;要选择信任并安装驱动。 安装结束&#xff0c;打开软件&#xff0c;是未连接的状态。 此时打开电脑的设备管理器&#xff…

程序员男盆友给自己做了一款增进感情的小程序

前言 又是无聊的一天&#xff0c;逛GitHub的时候发现一个给女朋友做了一个互动微信小程序&#xff0c;据说女朋友更爱自己了&#xff0c;所以当晚。。。。给自己做了丰盛的晚餐&#xff0c;我当即点开立马开发粘贴复制起来&#xff0c;想到做的小程序可以和未来的女朋友增进感…

基于React开发的chatgpt网页版(仿chatgpt)

在浏览github的时候发现了一个好玩的项目本项目&#xff0c;是github大神Yidadaa开发的chatgpt网页版&#xff0c;该开源项目是跨平台的&#xff0c;Web / PWA / Linux / Win / MacOS都可以访问。非常有意思&#xff0c;本人就部署了一套&#xff0c;喜欢的同学可以体验一番。 …

快速教程|如何在 AWS EC2上使用 Walrus 部署 GitLab

Walrus 是一款基于平台工程理念的开源应用管理平台&#xff0c;致力于解决应用交付领域的深切痛点。借助 Walrus 将云原生的能力和最佳实践扩展到非容器化环境&#xff0c;并支持任意应用形态统一编排部署&#xff0c;降低使用基础设施的复杂度&#xff0c;为研发和运维团队提供…

干货 | 接口自动化测试分层设计与实践总结

接口测试三要素&#xff1a; 参数构造 发起请求&#xff0c;获取响应 校验结果 一、原始状态 当我们的用例没有进行分层设计的时候&#xff0c;只能算是一个“苗条式”的脚本。以一个后台创建商品活动的场景为例&#xff0c;大概流程是这样的(默认已经是登录状态下)&#…

FMCW雷达论文速览 | TRS 2023, 基于FMCW雷达的多天线高精度测距算法及性能分析

注1:本文系“最新论文速览”系列之一,致力于简洁清晰地介绍、解读最新的顶会/顶刊论文 TRS 2023 | High Accuracy Multi-antenna Ranging Algorithm and Performance Analysis for FMCW Radar 论文原文:https://ieeexplore.ieee.org/document/10309162 Z. Xu, S. Qi and P. Zh…

webgoat-(A1)SQL Injection

SQL Injection (intro) SQL 命令主要分为三类&#xff1a; 数据操作语言 &#xff08;DML&#xff09;DML 语句可用于请求记录 &#xff08;SELECT&#xff09;、添加记录 &#xff08;INSERT&#xff09;、删除记录 &#xff08;DELETE&#xff09; 和修改现有记录 &#xff…

springboot本地启动多个模块报错:Address already in use: JVM_Bind

目录 背景解决方法 背景 环境&#xff1a; jdk1.8 idea 2019.2.4idea本地启动多个模块联调时&#xff0c;提示报错&#xff1a; 错误: 代理抛出异常错误: java.rmi.server.ExportException: Port already in use: 9090; nested exception is: java.net.BindException: Addre…

SpringBoot系列之集成Redission入门与实践教程

Redisson是一款基于java开发的开源项目&#xff0c;提供了很多企业级实践&#xff0c;比如分布式锁、消息队列、异步执行等功能。本文基于Springboot2版本集成redisson-spring-boot-starter实现redisson的基本应用 软件环境&#xff1a; JDK 1.8 SpringBoot 2.2.1 Maven 3.2…

【算法-链表2】反转链表 和 两两交换链表节点

今天&#xff0c;带来链表相关算法的讲解。文中不足错漏之处望请斧正&#xff01; 理论基础点这里 反转链表 1. 思路 链表操作的本质是修改连接关系&#xff0c;本题我们需要反转链表&#xff0c;也就是每次都让当前节点的next指向自己的上一个。而题目给的是单链表&#xf…

【React-Native开发3D应用】React Native加载GLB格式3D模型并打包至Android手机端

【React-Native开发3D应用】React Native加载GLB格式3D模型并打包至Android手机端 【加载3D模型】**React Native上如何加载glb格式的模型**第零步&#xff0c;选择相关模型第一步&#xff0c;导入相关模型加载库第二步&#xff0c;自定义GLB模型加载钩子第三步&#xff0c;借助…

Modbus通讯模拟仿真环境的搭建

文章目录 一、概要二、所需工具介绍三、搭建虚拟仿真环境1.Modbus RTU虚拟仿真环境搭建1.1.虚拟串口工具&#xff08;VSPD&#xff09;使用1.2.虚拟从站工具&#xff08;ModSim32&#xff09;使用1.3.虚拟主站工具&#xff08;Modscan32&#xff09;使用1.4.更改虚拟从站工具&a…

【算法】第二代遗传算法NSGA-II优化SVR超参数模型

NSGA-II介绍 NSGA-II&#xff08;Non-dominated Sorting Genetic Algorithm II&#xff09;是一种多目标优化算法&#xff0c;用于解决具有多个冲突目标的优化问题。它通过模拟进化过程中的自然选择和遗传操作&#xff0c;逐步改进种群中的解&#xff0c;以找到一组尽可能好的解…

Halcon的 Filter (过滤)目录之add_Image算子

Halcon两个图像相加可以应用在图像融合的场景中。通过将两幅图像的亮度信息相加&#xff0c;可以生成一幅新的图像&#xff0c;使得图像的细节更加清晰&#xff0c;提高目标检测和识别的准确率。例如&#xff0c;在红外图像和可见光图像融合中&#xff0c;加法运算可以将两幅图…

Linux程序设计shell程序学习

目录 1、编写shell脚本&#xff0c;通过循环的形式在终端上打印出等腰梯形 2、编写一个bash脚本程序&#xff0c;用for循环实现将当前目录下的所有.c文件移到指定的目录下&#xff0c;最后在显示器上显示指定目录下的文件和目录。 3、自行编写 shell 脚本&#xff0c;实现从…

【JAVA学习笔记】66 - 本章作业(IO流)

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter19/src/com/yinhai/homework 1.使用File类和FileWriter类 (1)在判断e盘下是否有文件夹mytemp&#xff0c;如果没有就创建mytemp public class Homework01 {public static void main(String…

小程序游戏对接广告收益微信小游戏抖音游戏软件

小程序游戏对接广告是一种常见的游戏开发模式&#xff0c;开发者可以通过在游戏中嵌入广告来获取收益。以下是一些与小程序游戏对接广告收益相关的关键信息&#xff1a; 小程序游戏广告平台选择&#xff1a; 选择适合你的小程序游戏的广告平台非常重要。不同的平台提供不同类型…

塔望食研院|骆驼奶市场规模庞大,百亿体量,品牌升级!

自2022年12月塔望咨询开设塔望食品大健康行业与消费研究院&#xff08;简称塔望食研院&#xff09;栏目以来&#xff0c;塔望食研院以“为食品行业品牌高质量发展赋能”为理念&#xff0c;不断发布食品大健康行业研究、消费研究报告。塔望食研院致力于结合消费调研数据、企业数…

智能井盖传感器功能,万宾科技产品介绍

在国家治理方面&#xff0c;对社会的治理是一个重要的领域&#xff0c;一定要在推进社会治理现代化过程中提高市政府的管理和工作能力&#xff0c;推动社会拥有稳定有序的发展。在管理过程中对全市井盖进行统一化管理&#xff0c;可能是市政府比较头疼的难题&#xff0c;如果想…