最近开始看Cherno的C++视频,受益匪浅,记录下笔记:

No.1

vs有两种默认运行方式,debug或release,其中在项目的C++属性栏中,debug模式下里面的Optimization是默认关闭的,而release模式是默认开启的,Optimization会自行简化一些操作,加速程序运行,但是不适合Debug。

No.2

头文件是在预处理阶段编译的,实际编译时,Compiler编译的只有cpp文件,每一个cpp文件都会被单独编译,然后每一个cpp都会生成一个其对应的(vs生成的是.obj格式)文件。VS里面按ctrl + F7可以单独编译一个Cpp。

No.3

什么是Translation Unit The text of the program is kept in units called source files in this International Standard. A source file together with all the headers (17.4.1.2) and source files included (16.2) via the preprocessing directive #include, less any source lines skipped by any of the conditional inclusion (16.1) preprocessing directives, is called a translation unit. [Note: a C++ program need not all be translated at the same time. ] 举个例子: 有两个cpp文件,它们互相不包含,互不关联,那么这是两个translation unit,每一个unit分别包含了其cpp文件和cpp文件包含的头文件,会生成两个.obj文件。 如果两个cpp文件A和B, A中有这么一句代码#include “B.cpp”,尽管各自编译都能生成各自的.obj文件,那么这些加起来也只是一个translation unit,注意,在代码中不要写这种代码#include “B.cpp”,否则相当于把B的所有内容复制粘贴到了A中,再进行Link操作会出现函数重定义的问题,这也不是一个好的编码习惯。

No.4

#include<header.h> 的底层实现原理 实际上很简单,在预处理过程中,compiler会找到这个文件,然后把文件里面所有的内容复制粘贴到写了这一行代码的cpp中,所以#include<…>通常都写在前面,具体参见第五条。

No.5

C++的预处理是怎么实现的 生成预处理文件 通过vs的项目属性参数的设置,可以把所有的预处理过程,也就是预处理后得到的新的cpp文件,将其输出到文件夹,可以在项目属性配置页面->预处理器->预处理到文件,选择“是",不会生成.obj文件,会生成一个.i文件。 其实#include预处理执行只是预处理器先打开main.cpp,执行到#include"test.h"时,就直接把test.h文件里的内容搬过来,仅此而已,就是这么简单。

No.6

C++生成的.obj文件 前面提到过,每一个cpp文件在完成编译过程后,都会生成一个.obj文件,.obj文件是纯粹的2进制的机器语言,也就是Machine Code,纯粹的机器语言我们是无法阅读的,但通过VS2017,我们可以将.obj文件转换成一种汇编语言的.asm文件,可以通过阅读.asm文件来了解计算机具体干了什么。 具体设置可以在项目属性配置页面->输出文件->ASM列表位置编译文件输出方式,从无列表改成仅有程序集的列表(/FA)。勾选之后,再编译文件,会在对应的路径生成对应的.asm文件(assembly),里面记载了程序生成的机器汇编语言,而且勾不勾选Promotion选项,是Debug和Release模式的重要差别之一,勾选了之后,会发现生成的.asm文件会自动对很多步骤进行优化,生成的指令集明显比不勾选生成的指令集内容要少,日后有兴趣可以仔细研究一下这块内容。

No.7

Linker的作用有以下几点

  • 将所有的.obj文件合成起来,生成一个exe(即使只有一个.obj文件也需要Linker)
  • 需要通过Linker找到函数的入口

C++程序默认的入口是main函数,当然可以在Visual Studio中自行设定入口函数:项目属性->连接器->高级->入口点去配置

No.8

关于函数重定义 当Linker把不同的.obj文件Link起来时,对于用到的函数,会去寻找其定义函数(Compiler只会查询有没有声明,不会管定义)。 举个函数重定义的例子: 在头文件里声明和定义了函数,然后两个或以上的cpp引用了该头文件。 原因:因为头文件的引用,对其预处理相当于使用的复制粘贴,所以两个cpp都会包含相应的定义。解决办法有三种:

  • 第一种:将头文件的该函数,设置为static函数,这样每个使用它的cpp,函数的义范围就仅限于该cpp了。
  • 第二种:将头文件的该函数,设置为inline函数,这样实际上变成了预处理类似的式,不会再报错。
  • 第三种:也是最常用的一种,就是.h文件里面不实现函数定义,只包含函数声明,这样就把原来两个Translation Unit 变成了一个,两个CPP均有函数声明,但是共享一个函数定义