C++|变量、对象、对象成员初始化的一些细节
bigegpt 2024-09-22 00:50 4 浏览
变量初始化是指变量定义后的第一次赋值(声明、定义、赋值三者同时进行),此后的赋值一般称为数据的更新或修改(注意任何操作都是有时间成本的)。
任何变量不管有没有初始化或赋值,都会有一个随机值或垃圾值。
我们知道,世间万物都可以数据化表示,进一步可以比特化表示 ,也就是用一串串0100110010101001……来表示,而0、1的硬件实现就是一个用作开关的晶体管。
每一个0或1称为一个比特(bit,对应一个开关晶体管),8个比特是一个字节(byte,),称为一个内存单元(字节是最小的内存单位),内存单元在内存中线性顺序排列,可以随机访问。在C++中,一个字母就用一个字节来表示,数字可以用4或8个字节来表示。通过数据类型的定义和类型的声明,编译器知道需要从哪个内存单元(变量名对应内存地址)开始访问,访问到哪个位置(内存单元)结束(C中没有定义字符串,以'\0'标志结尾,磁盘文件以EOF标识)。
即使变量没有初始化,变量对应的地址值仍然对应一个内存单元,对应一串比特,这一串比特的值是不确定的、随机的,可以称为垃圾值。这些垃圾值不是程序员所期望的,所以未初始化的变量使用(在右值中引用)时会有不可预料的错误,所以编译器对部分数据类型有强制化要求初始化或默认初始化的行为。
对于指针,如果未初始化,其随机值不确定,也就是需要指向的内存单元不确定,更会引发不可预料的错误。
1 未初始化的变量在右值引用时会出现错误
int j;//此时未初始化,j为一随机值或垃圾值
int i = 0;
i = j+1;//未初始化的在右值中引用会引发错误
cout<<i<<endl; //输出-858993459
如果是指针未初始化直接引用,程序会直接崩掉,因为指向找不到内存单元地址。
2 强制要求初始化的部分
2.1 引用要求声明时必须同时初始化;
2.2 const常量(基本类型变量或对象)也必须在声明时同时初始化(对const对象只能调用const成员函数),因为是常量,不存在赋值更新,唯一的机会就是初始化时给定一个值。
3 建议初始化的部分
指针建议声明时初始化,或最近位置初始化;
4 编译器会自动初始化的部分
编译器会自动初始化未初始化的全局或静态变量;
5 局部变量为什么编译器不自动初始化
效率的考量,因为变量的赋值也是需要时间的;
6 数组的初始化
声明数组的同时可以对数组初始化:
float x[5] = { -1.1, 0.2, 33.0, 4.4, 5.05 };
初始化表的长度短于要被初始化的数组元素数目,那么剩余元素被初始化为0。
带有初始化的一维数组可以不定义长度:
int a[]={1,2,3,4,5}; //则默认数组大小为5
初始化可以使用上述的集合赋值,但初始化后需要再次修改时却不能再次使用这种集合操作。因为你无法再次用x做右值,因为x相当于一个指针常量,你如果使用x[i]做左值,此时的x[i]只是其中的一个元素而已:
float fn=1.1;
float x[5] = { -1.1, 0.2, 33.0, 4.4, 5.05 };
float y[] ={1.0,2,3,4,5};
//x = y; //错误,因为x不能做左值,它相当于一个指针变量
x[5] = 3.3; //溢出错误,因为元素序列从0开始,只有5-1个元素,
cout<<fn<<endl; //输出3.3,因为此时的x[5]的内存单元地址对应的是fn的内存地址,也就是x[5]与fn指向了同一内存单元,且因为都是float,也就是访问的内存单元长度相同,;所以可以被编译器解析,其值就是更新后的3.3。
7 对象的初始化
对象的初始化使用一种特殊的成员函数,也就是构造(constructor)函数,通过构造函数为对象分配空间,进行初始化。
当程序员没有定义并实现构造函数时,编译器会调用一个默认的构造函数。
类的设计者在定义一个类时如果没有定义任何成员函数,也会有默认的四个函数:默认的构造函数、默认的复制构造函数、析构函数和赋值运算符重载函数。
构造函数可以重载,让对象有多种初始化形式。
当数据成员不是普通的内置类型,而是某一个类的对象(数据成员是类对象的情形成为数据聚合),可能无法直接用赋值语句在构造函数体中为它赋初值,程序员要显式声明并定义一个构造函数。
构造函数还有一个与普通函数不同的地方,就是可以包含一个构造函数初始化列表。
构造函数初始化列表位于函数头和函数体之间。它以一个冒号开头,接着是一个以逗号分隔的数据成员列表。
每个数据成员的后面跟着一个放在圆括号中的对应于该数据成员的构造函数的实际参数表。
如IntArray的构造函数可写为
IntArray :: IntArray(int lh, int rh): low(lh), high(rh)//low、high是成员,lh、rh是对应的值
{ storage = new int [high - low + 1]; }
显然利用初始化列表可以提高构造函数的效率。在初始化的时候,同时完成了赋初始的工作。
有二种情况必须用初始化列表。
I 是数据成员中含有一些不能用赋值操作进行赋值的数据成员,例如常量、引用数据成员,或对象数据成员,这时必须在初始化列表中调用数据成员所属类型的构造函数来构造它们。
II 派生类在构造函数中要对自身成员初始化,也要对继承过来的基类成员进行初始化,当基类没有默认构造函数的时候,通过在派生类的构造函数初始化列表中调用基类的构造函数实现。
8 不能在类声明中初始化const数据成员
const数据成员的初始化只能在类构造函数的初始化表中进行,不能在构造函数中对他赋值。
常量的数据成员指得是那些在对象生成时给定了初值,在整个对象的生命周期中,该数据成员的值是不能变的。常量数据成员的值必须在构造函数的初始化列表中进行初始化。
class A
{
A(int size);//构造函数
const int SIZE;
}
A::A(int size) : SIZE(size)//构造函数的初始化表
{…}
A a(100);//对象a的SIZE的值为100
A b(200);//对象b的SIZE的值为200
9 不能在类的构造函数中声明静态成员数据
静态成员变量的初始化不能放在类的构造函数中。
为静态成员分配空间称为静态成员的定义,静态成员的定义一般出现在类的实现文件中。如在SavingAccount类的实现文件中,必须要如下的定义:
double SavingAccount::rate = 0.05;
该定义为rate分配了空间,并给它赋了一个初值0.05。
如果没有这个定义,连接器会报告一个错误。
静态成员数据只能用静态成员函数来访问。静态的常量数据成员是整个类所有对象共享的一个常量。对整个类而言,不管定义了多少个对象,该成员永远只有一份拷贝。静态常量数据成员的值是在定义类时给定。
-End-
相关推荐
- Dify「模板转换」节点终极指南:动态文本生成进阶技巧(附代码)Jinja2引擎解析
-
这篇文章是关于Dify「模板转换」节点的终极指南,解析了基于Jinja2模板引擎的动态文本生成技巧,涵盖多源文本整合、知识检索结构化、动态API构建及个性化内容生成等六大应用场景,助力开发者高效利用模...
- 我用C#造了个AI程序员:自动调试+重构代码实战
-
在软件开发的世界里,调试和重构代码往往占据了程序员大量的时间。我一直梦想着能有一个智能助手,帮我处理这些繁琐的工作。于是,我决定用C#打造一个AI程序员,让它具备自动调试和重构代码的能力。系统架构设计...
- 公文自动排版vba代码(公文自动排版vba代码)
-
Sub公文自动排版()'设置页面参数(单位:厘米)WithActiveDocument.PageSetup.TopMargin=CentimetersToPoints(3.7)...
- Anthropic最强代码神器:Claude Code系统提示词
-
最近,在融合Opus-4之后,ClaudeCode的整体能力直线飙升.甚至一度把曾经的最强开发工具——Cursor打的抬不起头来。无论是代码生成的准确度,还是智能补全的丝滑体验,都让人印象深...
- 使用 Ruff 进行 Python 代码格式化与静态检查
-
随着Python项目的规模增大,保持一致的代码风格和高质量的代码变得尤为重要。Ruff是一个现代、高性能、支持lint和格式化的Python工具,能帮助你快速发现并修复常见代码问题。本文...
- 基础语法篇:格式化输出 含完整示例代码
-
所谓格式化输出就是按照一定格式来输出对应的内容,在Python的语法中格式化输出包含两种:格式化符号、格式化字符串一、格式化符号常用的格式化符号包括%s(将内容转换为字符串,放入占位位置)、%d(将内...
- 代码整洁如诗!Keil 插件上线,一键格式化代码,告别风格混乱!
-
引言:代码格式不统一?你的团队还在为“括号位置”吵架吗?嵌入式开发者们,你是否经历过这些抓狂瞬间?代码风格“百花齐放”:同事的代码缩进用空格,你的用Tab,合并时冲突频发!手动调整耗时费力:为了通过C...
- [信捷PLC] 信捷PLC之C函数编程(一)
-
前言写PLC程序,越来越觉得结构化文本编程语言(ST)给PC编程带来的便利,在处理一些数据上,可以写的更加灵活。所以,在项目PLC选型上,我都会优先选择支持结构化文本的PLC。国内有些厂商推出了一些较...
- C语言-HelloWorld解析(c语言的helloworld怎么写)
-
使用VisualStudio2017开发工具新创建一个项目,编写第一个C语言程序。#include<stdio.h>voidmain(){printf("HelloW...
- VSCode 配置 C++ 开发环境!教程详解
-
第一步、安装VSCode应用程序打开VSCode官网,下载对应安装包并默认安装(这里指明:安装路径可以修改)第二步、安装相关插件此时的VSCode仅仅是一个英文文本编辑器,还称不上开发工具,所以需要...
- C语言进阶教程:C语言与汇编语言交互
-
C语言和汇编语言的交互是底层编程和性能优化中的一个重要方面。理解它们如何协同工作,可以帮助开发者更好地控制硬件、优化关键代码段以及理解编译器的行为。为什么需要在C语言中嵌入汇编?尽管C语言已经提供了相...
- C语言如何处理平台相关代码(c语言的开发平台)
-
在进行跨平台C编程时,不可避免地会遇到需要针对不同操作系统或硬件架构编写特定代码的情况。C语言通过预处理器指令,特别是条件编译指令,为我们提供了处理平台相关代码的有效机制。最常用的就是利用预定义的宏(...
- C语言:hello world(c语言helloworld代码)
-
环境:a.初学者建议用“啊哈C”,这款软件简单易装;b.devc.visualstdiod.Vc6.0第一行代码:#include<stdio.h>#<stdio.h&g...
- C语言之编译器集合(编写c语言编译器)
-
C语言有多种不同的编译器,以下是常见的编译工具及其特点:一、主流C语言编译器1.GCC(GNUCompilerCollection)特点:开源、跨平台,支持多种语言(C、C++、Fortran...
- 适合零基础初学者学习C语言第一课教程,揭开C语言的神秘面纱
-
一、C语言简介我刚接触编程,首先想要学习的就是C语言,这次我就把我的感悟用我自己理解的文字表述出来,这样对刚学C语言的人来说,才是比较友好的。因为我们都没有C语言的基础,不懂啥是编程,啥事代码。我们...
- 一周热门
- 最近发表
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- libcrypto.so (74)
- linux安装minio (74)
- ubuntuunzip (67)
- vscode使用技巧 (83)
- secure-file-priv (67)
- vue阻止冒泡 (67)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)