条款4:确保对象在使用之前被初始化

文章目录

  • 内置类型
  • 非局部静态对象
  • 总结

内置类型

 C/C++ 中运行无初值的对象,是否确保对象初始化的规则很复杂。一般来说,如果使用C++的C部分,并且初始化可能会导致运行时成本,则不能保证会发生。如果使用C++的非C部分,情况有时会不同。例如:数组不一定保证其内容初始化,而vector的内容必须初始化。
最佳方法是始终在使用对象之前初始化它们。对于内置类型的非成员对象,需要手动完成。如:

int x = 0; // 手动初始化int类型
const char * text = "A C-style string"; // 指针的手动初始化
double d; // 通过读取输入流来“初始化”
std::cin >> d; 

对于几乎所有其他的情况下,初始化的责任都落在构造函数身上。

class PhoneNumber { ... };
class ABEntry { // ABEntry = "Address Book Entry"
public:ABEntry(const std::string& name, const std::string& address,const std::list<PhoneNumber>& phones);
private:std::string theName;std::string theAddress;std::list<PhoneNumber> thePhones;int numTimesConsulted;
};
ABEntry::ABEntry(const std::string& name, const std::string& address,const std::list<PhoneNumber>& phones){theName = name; // 这些都是赋值而不是初始化,theAddress = address; thePhones = phones;numTimesConsulted = 0;
}

 初始化发生在更早的时候——它们的默认构造函数在进入ABEntry构造函数体之前被自动调用。numTimesConsulted是个例外。内置类型不能保证在赋值之前对它进行了初始化。
下面是更好(更高效)的处理办法:

ABEntry::ABEntry(const std::string& name, const std::string& address,const std::list<PhoneNumber>& phones): theName(name),theAddress(address), // 现在都是初始化thePhones(phones),numTimesConsulted(0)
{} // 现在函数体是空的

数据成员按照在类中声明的顺序初始化。与初始化列表中的顺序无关。

非局部静态对象

什么是非局部静态对象?

  • 这些对象声明在函数外部或类的静态成员中。
  • 生命周期贯穿整个程序运行期间,直到程序结束才被销毁。
  • 非局部静态对象通过 static 关键字在全局作用域内声明,或者在类中声明静态成员变量。
static int globalStaticVar; // 全局静态变量
class Example {
public:static int staticMember; // 类的静态成员变量
};

局部静态对象

  • 这些对象声明在函数或块的内部。
  • 生命周期从第一次使用到程序结束之间,在声明的函数中不同于非静态局部对象,它不会在函数退出时销毁,而是保持状态直到程序结束。
  • 声明时使用 static 关键字。
void exampleFunction() {static int localVar = 0; // 静态局部变量++localVar;
}

 直接使用非局部静态对象的时候可能会存在一个问题,问题:如果一个编译单元中的非局部静态对象的初始化使用了另一个编译单元中的非局部静态对象,那么它所使用的对象可能是未初始化的,因为不同编译单元中定义的非局部静态对象初始化的相对顺序是未定义的。

class FileSystem { // 从你的库
public:...std::size_t numDisks() const; // 众多成员函数之一...
};
FileSystem tfs; extern FileSystem tfs; 	// 供客户(使用者)使用的对象;// "tfs" = "the file system"

现在假设某些客户创建了一个新类,用于处理文件系统中的目录。很自然,它们的类使用tfs对象:

class Directory { // 由库的客户(使用者)创建
public:Directory(params);...
};
Directory::Directory(params)
{...std::size_t disks = tfs.numDisks(); // 使用tfs对象...
}
Directory tempDir(params); // 临时文件目录

除非tfs在tempDir之前初始化,否则tempDir的构造函数将尝试在tfs初始化之前使用它。但它们的顺序无法确定。
解决办法:将非局部静态对象替换为局部静态对象。
将每个非局部静态对象移动到自己的函数中,并将其声明为static。这些函数返回它们包含的对象的引用。客户调用函数而不是引用对象。
依据:C++保证,函数内的局部静态对象会在调用该函数时、第一次遇到该对象的定义时初始化

class FileSystem { ... }; // 和以前一样FileSystem& tfs() // 这将替换tfs对象;在FileSystem类中,它可以是静态的
{ static FileSystem fs; 	// 定义/初始化一个局部静态对象return fs; 		// 返回对它的引用
}
class Directory { ... }; // 和以前一样
Directory::Directory(params) // 与之前一样,除了对TFS的引用现在变为tfs ()
{  ...std::size_t disks = tfs().numDisks();...
}
Directory& tempDir() // 这将替换tempDir对象;它可以是Directory类中静态函数
{ static Directory td; 	//定义/初始化局部静态对象return td; 		// 返回指向它的引用
}

在多线程程序中,最好还是在单线程启动阶段手动调用函数完成初始化。

总结

  • 手动初始化内置类型的对象,因为C++不确保会初始化它们。
  • 在构造函数中,优先使用成员初始化列表,而不是在构造函数体内赋值。以类中声明的顺序在初始化列表中列出数据成员。
  • 通过将非局部静态对象替换为局部静态对象,避免跨编译单元的初始化顺序问题。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入理解前端项目中的 package.json

在前端开发中&#xff0c;package.json 是一个很重要的文件&#xff0c;它在Node.js和前端项目中扮演着重要的角色。这个文件用于存储项目的元数据以及管理项目的依赖关系。 package.json 文件是每个Node.js项目和许多前端项目的核心。它不仅定义了项目的基本属性&#xff0c;…

Docker笔记:配置docker网络与不通网络之间的通信及跨主机通信

配置 docker 网络 1 &#xff09; Docker0 网络 默认启动的所有容器&#xff0c;都会加入到这个网络中在有docker环境的 centos 中&#xff0c; 通过 $ ip addr 可以看到Docker0网络相关信息多个容器都在Docker0 网络中就属于同一个局域网了&#xff0c;就可以通信了启动三个…

ripro后台登录后转圈和图标不显示的原因及解决方法

最近&#xff0c;好多小伙伴使用ripro主题的小伙伴们都发现&#xff0c;登录后台后&#xff0c;进入主题设置就转圈&#xff0c;等待老半天后好不容易显示页面了&#xff0c;却发现图标不显示了&#xff0c;都统一显示为方框。 这是因为后台的js、css这类静态资源托管用的是js…

2312llvm,使用llvm的示例

原文 介绍 除此外,LLVM是编译器编写者的平台.因为其异常干净和小巧的IR(中间表示),使用LLVM编写编译器比其他系统容易得多. 作为证明,栈机的作者在大约四天内编写了整个编译器(语言定义,词法解析器,解析器,代码生成器等)!了解这一点很重要,因为它显示了使用LLVM时,可多快地获…

力扣刷题-二叉树-找树左下角的值

513 找树左下角的值 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1&#xff1a; 示例 2&#xff1a; 思路 层序遍历 直接层序遍历&#xff0c;因为题目说了是最底层&#xff0c;最左边的值&a…

紫光FPGA DDR3 IP使用和注意事项(axi4协议)

紫光DDR3 IP使用 对于紫光ddr3 IP核的使用需要注意事情。 阅读ddr ip手册&#xff1a; 1、注意&#xff1a;对于写地址通道&#xff0c;axi_awvalid要一直拉高&#xff0c;axi_awready才会拉高。使用的芯片型号时PG2L100H-6FBG676&#xff0c;不同的型号IP核接口和axi的握手协…

IDEA2020关于Cannot resolve symbol ‘servlet‘报错

刚开始也配置了tomcat&#xff0c;但是依然报错&#xff0c;后来查找资料解决了 在项目下面创建一个libs文件夹&#xff0c;然后将tomcat / lib文件夹中的servlet-api.jar复制了过来&#xff0c;然后再添加到library。 具体操作步骤&#xff1a;

Code automatic processing

自动化处理没啥用的代码&#xff0c;测试下&#xff0c;还不错的感觉

Elasticsearch的使用总结

Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。它能很方便的使大量数据具有搜索、分析和探索的能力。 put/post请求&#xff1a;http://localhost:9200/索引库名称 {"settings":{"index":{"number_of_shards":1, # 分片数量…

Axure的交互样式和情形

Axure的交互样式和情形 交互样式 Axure是一个流行的原型设计工具&#xff0c;它允许您创建交互式原型&#xff0c;模拟应用程序或网站的功能和用户界面。在Axure中&#xff0c;您可以设置各种交互样式来使原型更加生动和真实。 链接触发器&#xff1a;通过给一个元素添加链接…

风速预测(三)EMD-LSTM-Attention模型

目录 1 风速数据EMD分解与可视化 1.1 导入数据 1.2 EMD分解 2 数据集制作与预处理 2.1 先划分数据集&#xff0c;按照8&#xff1a;2划分训练集和测试集 2.2 设置滑动窗口大小为7&#xff0c;制作数据集 3 基于Pytorch的EMD-LSTM-Attention模型预测 3.1 数据加载&#…

记错vue3+ts require 报错

在main.js 中使用require 报错 ‘require’ is not defined 事先声明 &#xff0c;可能是版本不一样&#xff0c;所以解决办法不一样&#xff0c;报错的原因是Pack.json 文件中type值不同&#xff0c;解决办法有两种。 目前有什么弊端&#xff0c;因为时间比较紧&#xff0c;还…

现代C++的多线程开发

前言 早期的C进行多线程编程&#xff0c;往往需要根据不同的系统编写不同的代码&#xff0c;但是在C11之后&#xff0c;std中已经提供了多线程的支持&#xff0c;所以对于不同操作系统只需要编写一次代码即可。 本文记录一次多线程开发过程中&#xff0c;使用的C新特性&#…

uniapp怎么获取微信步数

微信步数获取的背景 微信步数是指用户在微信运动中记录的步数数据。微信提供了开放能力&#xff0c;允许第三方应用获取用户授权后的微信步数数据&#xff0c;以便进行进一步的数据分析和展示。使用时报错&#xff1a;fail api scope is not declared in the privacy agreemen…

【华为机试】2023年真题B卷(python)-洞穴探险

一、题目 题目描述&#xff1a; 某探险队负责对地下洞穴进行探险。探险队成员在进行探险任务时&#xff0c;随身携带的记录器会不定期地记录自身的坐标&#xff0c;但在记录的间隙中也会记录其他数据。探索工作结束后&#xff0c;探险队需要获取到某成员在探险过程中相对于探险…

libevent服务GET/POST的简单使用

目录 1、前言2、测试demo2.1、目录结构2.2、 测试源码2.2.1、http_server.cpp2.2.2、 http_server.h 2.3、 编译2.4、 运行结果2.4.1、测试POST2.4.2 、测试GET请求 1、前言 项目开发中经常需要使用到私有协议和Qt,Android等GUI前端通信&#xff0c;比较常用的使用POST和GET方式…

计算机操作系统-第十八天

目录 进程调度时机 补充知识 进程调度的方式 非剥夺调度方式 剥夺调度方式 进程的切换与过程 本节思维导图 进程调度时机 进程调度&#xff08;低级调度&#xff09;&#xff0c;即按照某种算法从就绪队列中选择一个进程为其分配处理机。 共有两种需要进行进程调度与…

基于junit4搭建自定义的接口自动化测试框架

随着业务的逐步稳定&#xff0c;对于接口的改动也会逐渐变少。更多的是对业务逻辑的优化&#xff0c;功能实现的完善。对于测试来说&#xff0c;重复繁琐的功能测试不仅效率低下&#xff0c;而且耗费一定的人力资源。笔者支持的信息流业务下的一个图文管理平台就是一个功能较为…

Lambda表达式的简单理解

1. 初识lambda表达式 Lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。 lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。 Lambda 表达式&#xff08;Lambda exp…

网络基础试题选择题——附答案

选择题 OSI模型中&#xff0c;表示层的作用是&#xff1f; A) 数据链路B) 数据传输C) 数据格式转换D) 物理连接 IP地址的IPv4版本中&#xff0c;一般由几个十进制数构成&#xff1f; A) 2B) 4C) 6D) 8 在HTTP协议中&#xff0c;常用的请求方法是&#xff1f; A) GETB) POSTC) P…