C++:杂记(代码编译器、初始化,C++17的一个特性)
前言
说到这些天,我深感要做到一个专业的C++程序员,我感觉还有更长路要走。有些焦虑不已,但不管是直接询问大佬的意见,还是看各种公众号文章,普遍给我的感觉是:要注重基础,所以我又重新啃C++之父的那本《C++程序设计语言》4版本了。在阅读中也有些新的感悟,记录于此。
码编译器
之前用的是vscode,换了电脑后,发现烦人配置又激起了我的回忆,找了之前的自己总结的配置文件,发现失效了。于是就网上走马观花地搜罗起来了。想不到真的让我找到了一款,而且它也是免费的,还是跨平台的(不管是Ubuntu,还是Windows都可以用),最重要的发现它也是主流代码编译器,生态也蛮丰富的,用户手册,论坛都挺齐全的,一下子映入我眼帘,这下可以把Microsoft Visual Studio这个庞然大物放一边了,这也是个轻量级代码编译器,它的官网的简洁风格,我蛮喜欢的。


特征
亮点:
- 开源!GPLv3,无隐藏费用。
- 跨平台。可在 Linux、Mac、Windows(使用 wxWidgets)上运行。
- 用 C++ 编写。无需解释语言或专有库。
- 可通过插件扩展
编译器:
-
多编译器支持:
- GCC(MingW / GNU GCC)
- MSVC++
- 铛
- 数字火星
- Borland C++ 5.5
- 打开 Watcom
- …以及更多
-
非常快速的自定义构建系统(无需 makefile)
-
支持并行构建(利用 CPU 的额外核心)
-
多目标项目
-
合并多个项目的工作区
-
工作区内的项目间依赖关系
-
导入 MSVC 项目和工作区(注意:尚不支持汇编代码)
-
导入 Dev-C++ 项目
调试器:
-
接口 GNU GDB
-
还支持 MS CDB(功能不全)
-
完整的断点支持:
- 代码断点
- 数据断点(读取、写入和读/写)
- 断点条件(仅当表达式为真时才中断)
- 断点忽略计数(仅在一定次数后中断)
-
显示局部函数符号和参数
-
用户定义监视(支持通过脚本监视用户定义类型)
-
调用堆栈
-
拆卸
-
自定义内存转储
-
在线程之间切换
-
查看CPU寄存器
界面:
- 语法高亮,可定制、可扩展
- C、C++、Fortran、XML 和更多文件的代码折叠。
- 选项卡式界面
- 代码补全
- 类浏览器
- 智能缩进
- .h 和 .c/.cpp 文件之间的一键切换
- 打开文件列表以便在文件之间快速切换(可选)
- 外部可定制的“工具”
- 不同用户的待办事项列表管理
并且通过插件提供更多功能!

初始化
C++中有5种常见的初始化,如下:
int a; // default-initialization (no initializer)
// Traditional initialization forms:
int b = 5; // copy-initialization (initial value after equals sign)
int c ( 6 ); // direct-initialization (initial value in parenthesis)
// Modern initialization forms (preferred):
int d { 7 }; // direct-list-initialization (initial value in braces)
int e {}; // value-initialization (empty braces)
第一种a是默认初始化,大多数情况下,这种初始化不执行任何初始化,而是给变量留下一个不确认的值,被称为“垃圾值”,这往往致使意想不到的结果,比如程序每次运行会得到不同的结果,有时候正确,有时候不正确,而且这种错误最难调试,所以要非常重视初始化问题。
第二种b(带等号的),是拷贝初始化,这种初始化形式继承于C语言,和拷贝复制一样,是将右边的值赋值给左侧创建的变量。复制初始化在现代 C++ 中已不再受欢迎,因为它对于某些复杂类型而言效率低于其他初始化形式。然而,C++17 解决了大部分此类问题,拷贝初始化如今又找到了新的拥护者。
第三种c(带中括号的),是直接初始化,直接初始化最初是为了更高效地初始化复杂对象而引入的。与拷贝初始化一样,直接初始化在现代 C++ 中已经不再受欢迎,主要是因为它被直接列表初始化所取代。然而,直接初始化本身也有一些怪癖,因此在某些情况下,直接初始化再次得到了应用。
第四种d(带花括号的),是列表初始化,共有两种,另一种是在其花括号前面加上=号,即“int d { 7 };”。在 C++11 之前,某些类型的初始化需要使用拷贝初始化,而其他类型的初始化则需要使用直接初始化。拷贝初始化与拷贝赋值可能难以区分(因为两者都使用=)。直接初始化与函数相关操作也难以区分(因为两者都使用括号)。没错,这就是为了区分初始化而建立的一个值。
第五种e(空花括号的),是一种特殊的列表初始化,因为当使用空括号初始化变量时,会发生一种特殊形式的列表初始化,称为值初始化。在大多数情况下,值初始化会隐式地将变量初始化为零(或给定类型的最接近零的值)。如果发生归零,则称为零初始化。
第四和第五,是现代C++中推荐的写法。引入列表初始化是为了提供一种在几乎所有情况下都有效、行为一致且具有明确的语法的初始化语法,使我们很容易知道在哪里初始化对象。而且更重要的一个原因是它更安全,能够防止窄化。窄化就是数据从一个类型转换为另一个类型时,数据发生截断或者是丢失精度的现象。比如:double->float,可能存在窄化,int->char,可能存在窄化。
#include <iostream>
using namespace std;
int main()
{
int i = 3.4;
cout << i << endl;//3
return 0;
}

#include <iostream>
using namespace std;
int main()
{
int j {3.4};
cout << j << endl;
return 0;
}

C++中也不乏类型的隐式转换,而这会一定情况下会使数据精度丢失,在某些对数据精确度要求高的领域,这种错误往往是致命的,所推荐的列表初始化能够防止窄化,保证数据安全。
关于窄化转换的补充:
- 如果一种整型存不下另外一种整型的值,则后者不会转换为前者。例如,允许char到int的类型转换,但是不允许int到char的类型转换。
- 如果一种浮点型存不下另外一种浮点型的值,则后者不会转换为前者。例如,允许float到double的类型转换,但是不允许double到float的类型转换。
- 浮点型的值不能转化换成整型值
- 整型值不能转化换成浮点型的值
关于值,对象,和变量。
值:在编程中,单个数据被称为一个值(有时也称为数据值)。值的常见示例包括:
- 数字(例如5或-6.7)。编译器中,数值不加引号。
- 字符,放在单引号内(例如’H’或’$')。只能使用一个符号。编译器将单引号中的值解释为字符值。
- 文本,必须放在双引号内(例如"Hello"或"H")。文本可以包含 0 个或多个字符。编译器将双引号中的值解释为文本值。
直接放入源代码的值称为字面量。
对象:表示一块可以保存值的存储区域(通常是 RAM 或 CPU 寄存器)。
变量:在一般编程中,“对象”通常指内存中的未命名对象、变量或函数。在 C++ 中,“对象”的定义更为狭义,不包括函数。
C++17的特性---------[[maybe_unused]]:解决unused variable error
#include <iostream>
using namespace std;
int main()
{
int j {3};//or int j ={3};
return 0;
}

C++当你合理初始化一个变量之后,不用它竟然也会报错(某个程序佬说,把警告也视为一种错误,这是一种强制自律的方法)。
最简单的就是删除它,或者去打印它。如果我们经常使用这些值有很多个,可能会把它们一起保存在某个地方并将它们粘贴/复制/导入进来,然而,在任何我们不使用所有这些值的程序中,编译器可能会对每个实际上未使用的变量报错。少的时候我们可以轻松地删除不用 的定义。但是,如果有 20 或 30 个变量而不是 3 个呢?如果我们在多个地方使用它们呢?浏览变量列表以删除/注释掉未使用的变量会耗费时间和精力。而且,如果我们以后需要使用之前删除的变量,我们将将不得不花费更多的时间和精力返回并重新添加/取消注释它。
#include <iostream>
using namespace std;
int main()
{
[[maybe_unused]] int j {3};
return 0;
}

为了解决此类情况,C++17 引入了[[maybe_unused]]属性,它允许我们告诉编译器,我们可以接受某个变量未被使用。编译器不会针对此类变量生成“未使用变量”警告。
更多推荐


所有评论(0)