跳过正文
  1. 文章/

cpp学习笔记(1)

··1468 字·3 分钟
目录

前言
#

最近找到个个还不错的网站,适合我这种大白,遂开始学习,记录一些感受。

此系列可能没有很好的排版,因为是给自己看的。

不保证所有的内容一定正确,因为是学习过程中记录的,所以可能还会有完全不正确的理解。

澄清一个我混淆的概念
#

表达式:求值,调用函数,变量/常量和运算符相结合的东西

语句:有分号的东西,可以由多个表达式复合而成。

statement expression(原谅我不会翻译):只含一个表达式的语句。

标识符:用户自定义的名称,不以数字开头。

关键字:系统保留字,用于流程控制。

cin/cout探秘
#

我一直好奇,<<是怎么实现的,还能同时接着,是怎么使用的。

事实上,这运用的是运算符重载,语义分析会处理左右两边的表达式,然后决定是否重载它。

<<是左结合的,所以优先处理左边的部分。<<一看到左边是一个ostream类型,右边是一个字符串,自动重载,通过运算返回一个ostream,接着再处理次级优先的,依次拼接一个完整的字符串流。

这里提到一个重要概念:副作用。

正常来说,一个函数应该要返回值。当我们只使用一个<<的时候,确实只用到了副作用,如果我们连在一起使用的时候,那么返回值就非常重要了。这是不是有点像fp,给定输入得到输出,接着成为另一个输入?

现代的初始化
#

现在c++推荐使用列表直接初始化,避免隐式类型转换。

如:foo{}

还有别的办法:

  • 直接初始化:foo(1)
  • 间接列表初始化:foo = {1}

⚠️不推荐在同一行定义多个变量,因为会在git中导致diff不清晰。

literal/variable
#

学过微机的人都知道,一个程序加载进内存,会分别放到代码段和数据段,接着获取到一些段(stack,pile)。

其中:

  • 代码段是只读的,同时和堆栈段中间还相隔了一些部分,这是避免数据溢出破坏程序的运行。
  • 对于字面量来说,使用它,就直接到数据段里面取,没有分配给他额外的空间。
  • 而对于变量来说,使用它,先得从数据段中拷贝一份到栈/堆(假设初始化的话),接着读取这个地址指向的内容,才能进行访问。

compile-time / runtime
#

什么是编译时和运行时的概念?

编译时:编译器在进行语法检查的时候。

运行时:程序真正运行的时候。

所以说,我们有下面的内容:

  1. 标识符和编译时相对应,对象和运行时相对应。

  2. 作用域(scope)和编译时对应,生命周期(lifetime)和运行时相对应。

  3. 同时还有一个大杀器:constexpr

    这个关键字的意思,指的是能够在编译时就确定常量(甚至函数),而不是运行时,从而把运行时开销转换成编译时间。

    现在,对于简单的函数,不含new/deletecin/cout,随机数生成的函数,我们都推荐使用constexpr

    需要注意的是,如果一个常量是在块作用域中,而且初始化用的是常量表达式({} 零初始化 是常量表达式),那么它会自动成为常量表达式。

    需要注意的是,在C++11~20的不同版本中,支持的类型略有差异,比如后期才支持流程控制的for/while/if。

感觉gemini这么说不错:

标识符是编译时的抽象,是对象在代码中的名字和类型。 对象是运行时的实体,是标识符所代表的内存和数据。

cmake的一些使用方法(对于多文件/引入libs)
#

  1. 加入这一行把需要的头包含进来,就可以直接在#include里面写.h的文件了,无须写相对路径。

    target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/)
    
  2. 在cmake 3.30之后,引入Boost直接用find_package(Boost)就好。

Best Practise
#

  1. 尽量在使用一个变量的正前面初始化。

  2. 尽量不要使用无符号整形,因为其具有的环绕特性会在一些行为(比如与有符号整数比较)上面让人难以调试。