前言

说到这些天,我深感要做到一个专业的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]]属性,它允许我们告诉编译器,我们可以接受某个变量未被使用。编译器不会针对此类变量生成“未使用变量”警告。

    Logo

    Agent 垂直技术社区,欢迎活跃、内容共建。

    更多推荐