当前位置: 首页 > news >正文

条款04:确定对象被使用前已先被初始化

C++并不能保证每个对象在定义时都被自动初始化。就像书中第一条提到的一样,C++包含多种子语言,例如定义一个C风格的整型数组(int[])时,其中就可能包含非零初始化的元素,而在定义标准库(STL)中的容器时,例如一个整型向量(std::vector),其强大的函数库可以保证所有元素都被零初始化。
自有类型(built-in type)的初始化
C++的自有类型继承于C,因此不能保证此类型的变量在定义时被初始化。使用未初始化的数据可能会导致程序不正常运作,因此在定义变量的时候,需要对其进行初始化,例如将如下代码:

intx;doubled;

改为:

intx=0;doubled;std::cin>>d;

类的初始化
对于用户自定义的类,我们需要构造函数(constructor)来完成此类的初始化,例如:

classCoordinate{private:intx;doubley;conststd::list<double>&num;public:Coordinate(constint&_x,constint&_y,conststd::list<double>&_num);};//以下构造函数为成员x, y, num赋值来完成对象的初始化Coordinate::Coordinate(constint&_x,constint&_y,conststd::list<double>&_num){x=_x;y=_y;num=_num;}

可能这是一个易于记忆的方法,但并不是最好的方法,因为此构造函数并没有真正完成“初始化”,只不过是做了“赋值”的操作。而C++规定,在进入构造函数之前,如果用户没有规定初始化过程,C++将自动调用各成员对应类型的默认构造函数。
这样一来,此构造函数就相当于先调用了C++的默认构造函数,又做了一次赋值操作覆盖掉了先前的结果,造成了浪费。
解决方法:使用初始化列表(initialization list),C++就不必额外调用默认构造函数了

Coordinate::Coordinate(constint&_x,constint&_y,conststd::list<double>&_num):x(_x),y(_y),num(_num){}

另外,**构造函数是可以被重载(overload)的,**对于这个我们自己定义的类,还需要一个没有参数输入的默认构造函数,因此我们可以定义:

Coordinate::Coordinate():x(0),y(0),num(){}//num()调用了std::list<double>类型的默认构造函数

某些初始化是语法必要的
例如在定义引用(reference)和常量(const)时,不将其初始化会导致编译器报错

constinta;//报错,需要初始化!int&b;//报错,需要初始化!//现在对其进行初始化:constinta=3;//编译通过intc=3;int&b=c;//编译通过!

数据初始化的顺序
在继承关系中,基类(base class)总是先被初始化。
在同一类中,成员数据的初始化顺序与其声明顺序是一致的,而不是初始化列表的顺序。因此,为了代码一致性,要保证初始化列表的顺序与成员数据声明的顺序是一样的。

classmyClass{private:inta;intb;intc;public:myClass(int_a,int_b,int_c);};//注意,即使初始化列表是c->b->a的顺序,真正的初始化顺序还是按照a->b->cmyClass::myClass(int_a,int_b,int_c):c(_c),a(_a),b(_b){}

初始化非本地静态对象
现在还有一种特殊情况,尤其是在大型项目中比较普遍:在两个编译单元中,分别包含至少一个非本地静态对象,当这些对象发生互动时,它们的初始化顺序是不确定的,所以直接使用这些变量,就会给程序的运行带来风险。
先简要解释一下概念,
编译单元(translation unit): 可以让编译器生成代码的基本单元,一般一个源代码文件就是一个编译单元。
非本地静态对象(non-local static object): 静态对象可以是在全局范围定义的变量,在名空间范围定义的变量,函数范围内定义为static的变量,类的范围内定义为static的变量,而除了函数中的静态对象是本地的,其他都是非本地的。(非局部静态变量就是非本地静态变量:例如:全局对象,全局静态变量等)
此外注意,静态对象存在于程序的开始到结束,所以它不是基于堆(heap)或者栈(stack)的。初始化的静态对象存在于.data中,未初始化的则存在于.bss中。
回到问题,现有以下服务器代码:

classServer{...};externServer server;//定义,在全局范围声明外部对象server,供外部使用

又有某客户端:

classClient{...};Client::Client(...){number=server.number;}Client client;//在全局范围定义client对象,自动调用了Client类的构造函数

以上问题在于,定义对象client自动调用了Client类的构造函数,此时需要读取对象server的数据,但全局变量的不可控性让我们不能保证对象server在此时被读取时是初始化的。试想如果还有对象client1, client2等等不同的用户读写,我们不能保证当前server的数据是我们想要的。
解决方法: 将全局变量变为本地静态变量
使用一个函数,只用来定义一个本地静态变量并返回它的引用。因为C++规定在本地范围(函数范围)内定义某静态对象时,当此函数被调用,该静态变量一定会被初始化。

classServer{...};Server&server(){//将直接的声明改为一个函数staticServer server;returnserver;}
classClient{...};Client::client(){//客户端构造函数通过函数访问服务器数据number=server().number;}Client&client(){//同样将客户端的声明改为一个函数staticClient client;returnclient;}

总结:
对于自由类型,要保证在定义时手动初始化
在定义构造函数时,要用初始化列表,避免使用在函数体内的赋值初始化。在使用初始化列表时,为了保持代码一致性,初始化列表中变量的顺序 要与其声明顺序相同
当不同的编译单元产生互动时,要将其中非本地的静态变量变为本地的静态变量才能保证安全的读写(也可以在client类里面私有成员变量定义一个sever对象)(在公司这样用过)

对引用的理解:
引用是无所有权无空值可能的“指针”,总是指向别的对象。
引用不对内存管理负责,那总是别人的事,无权即无责。
成员变量也可以为引用,(我觉得应该少用),使用起来风险较高,可能会指向一个不存在的内存,引起不确定性问题,虽然编译器会做一定程度的检测,但还是有绕过的可能,举例:

classWidget{size_t&cap;public:Widget(size_t&cap):cap(cap){}size_tgetCap(){returncap;}};Widget*getWidget(){size_tcap(10);Widget*w=newWidget(cap);cout<<w->getCap()<<endl;// 打印:10returnw;}intmain(intargc,constchar*argv[]){Widget*w=getWidget();cout<<w->getCap()<<endl;// 打印:4301990432(随机值)return0;}
http://www.jsqmd.com/news/668904/

相关文章:

  • 【流量分析】Wireshark v4.6.4
  • AGI去中心化不是理想主义——全球首个通过ISO/IEC 27001认证的分布式推理网络架构解密(含审计报告编号:AGI-DC-2024-089)
  • c语言实例|实现简单的命令行
  • 正点原子达芬奇FPGA运动目标检测仿真代码:ov5640配置与数据输出,RGB转YUV,帧差、...
  • 浅析golang中的垃圾回收机制(GC)
  • 为什么顶尖AI实验室已暂停通用模型迭代?SITS2026圆桌闭门纪要首度外泄:AGI自主演化证据链+人类控制窗口期剩余≤11个月
  • 告别ImageMagick卡顿!试试这个更快的图片处理神器GraphicsMagick,附CentOS 7保姆级安装教程
  • 贵阳找工作怎么办?毕业季困局与破局:贵阳应届生的求职地图 - 精选优质企业推荐官
  • golang如何调用Twilio语音短信API_golang Twilio语音短信API调用实战
  • CSS如何实现跨容器的连线效果_利用绝对定位的线条结合宽高与旋转角度连接两个节点
  • 【项目实战】基于语言大模型的智能居家养老健康守护系统后端:情感陪伴 Agent 开发与全功能测试报告
  • [K8s/本地存储] Kubernetes 本地存储进化史:从 hostPath 到 local-path-provisioner
  • 定义层间接触
  • 汽车零部件企业ERP数字化转型实践:基于SAP Business One的落地经验
  • 贵阳招聘市场风向标:2026年最值得关注的12家公司与岗位机会分析 - 精选优质企业推荐官
  • 告别RPM/Yum:为什么我选择用tar.xz源码包在Linux上部署MySQL 8.0?
  • 2026年沈阳婚纱照排名大揭秘,哪家才是你的心头好?
  • 多客圈子论坛代码审计(PHP代码审计)
  • 【AGI政策制定黄金72小时】:从奇点大会技术共识到地方条例起草的实战操作手册
  • 欠驱动无人船AUV二维路径跟踪控制(反步控制+LOS制导)MATLAB仿真
  • C++:全景目录
  • 贵阳招聘市场2026年全景盘点:10大竞品对比与求职指南 - 精选优质企业推荐官
  • 【仅限前200名技术决策者获取】:2026奇点大会AGI气候预测引擎API接口规范及部署手册(含实测减排误差<0.8%的基准测试数据)
  • 从CSP-J真题到算法实战:拆解‘鸡蛋硬度’问题的递归与动态规划双视角
  • 如何在Unity中5分钟内实现专业级3D高斯泼溅渲染
  • 2026创新项目实训-项目博客(三)
  • 嵌入式消费品商业开发需求导出与便捷调试
  • SpringBoot+Vue企业人事管理系统源码+论文
  • 5G手机第一次联网时,基站是怎么知道你在哪个方向的?聊聊PRACH Occasion与波束的‘暗号’映射
  • Substance 3D Painter Pt 2025 v11.0.1详细图文安装教程