本 Wiki 开启了 HTTPS。但由于同 IP 的 Blog 也开启了 HTTPS,因此本站必须要支持 SNI 的浏览器才能浏览。为了兼容一部分浏览器,本站保留了 HTTP 作为兼容。如果您的浏览器支持 SNI,请尽量通过 HTTPS 访问本站,谢谢!
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录前一修订版后一修订版 | 前一修订版 | ||
cs:programming:cpp:courses:cpp_basic_deep:chpt_0 [2024/01/23 15:20] – codinghare | cs:programming:cpp:courses:cpp_basic_deep:chpt_0 [2024/04/14 11:45] (当前版本) – [编译链接模型] codinghare | ||
---|---|---|---|
行 13: | 行 13: | ||
==对象生命周期的精确控制== | ==对象生命周期的精确控制== | ||
* C++ 没有垃圾回收机制,需要处理资源 | * C++ 没有垃圾回收机制,需要处理资源 | ||
- | * Zero-overhead Abstraction | + | * 垃圾回收需要额外的系统资源运行 |
- | * 引入大量特性,便于工程实践 | + | C++ 中的异常处理 只有 try-catch, C# 中有 try-catch-finally. 因为垃圾回收必须要使用 finally。 |
+ | ==Zero-overhead Abstraction== | ||
+ | * 不需要为没有使用的语言特性付出成本 | ||
+ | * 虚函数:没有虚函数,就不是抽象类(对应 C# | ||
+ | * https:// | ||
+ | * 不用 new 就不用堆 (C# 会) | ||
+ | * 使用了一些语言特性不等于付出运行期成本 | ||
+ | * https:// | ||
+ | * 编译期已经处理了函数逻辑 | ||
+ | * '' | ||
+ | |||
+ | ==引入大量特性,便于工程实践== | ||
+ | * 一系列不断衍进的标准集合 | ||
+ | * C++98/03 , C++11 , C++14 , C++17 , C++20 , C++23 ? | ||
* 三种编程范式:面向过程、面向对象、泛型 | * 三种编程范式:面向过程、面向对象、泛型 | ||
* 函数重载、异常处理、引用 | * 函数重载、异常处理、引用 | ||
+ | * 语言本身的改进 | ||
+ | * Memory Model(多线程角度 C++ 11) | ||
+ | * Lambda Expression(C++11) | ||
+ | * 标准库的改进 | ||
+ | * type_traits / ranges(容器扩展) | ||
+ | * auto_ptr(C++11 中被智能指针替代) | ||
+ | ==C++ 标准的工业界实现== | ||
+ | * MSVC / GCC / Clang | ||
+ | * 每个编译器可能并不完全遵照标准 | ||
+ | * https:// | ||
+ | * 不同的实现存在差异 | ||
+ | * https:// | ||
+ | ==不能脱离具体的语境讨论 C++== | ||
+ | * 我使用什么样的标准 | ||
+ | * 我使用什么样的工具 | ||
+ | ==编写程序时要注重== | ||
+ | * 性能 | ||
+ | * 标准:尽量使用跨平台的库(符合标准的库),避免移植问题 | ||
+ | ====C++ 的开发环境与相关工具==== | ||
+ | * 编译器:Visual C++ / GCC (G++) / Clang (Clang++) | ||
+ | ===工具=== | ||
+ | * time: 使用 linux 自带的 time 测试程序运行时间 | ||
+ | <code bash> | ||
+ | / | ||
+ | </ | ||
+ | * valgrind:查内存泄漏 | ||
+ | * Cpp reference | ||
+ | * Compiler explorer | ||
+ | * 可以查看程序对应的汇编代码 | ||
+ | * 代码的分颜色:对应汇编和C++源码 | ||
+ | * 可选不同编译器,方便做比较 | ||
+ | * C++ Insights:解释代码(比如 for range 是怎么实现的) | ||
+ | * youtube | ||
+ | * cppcon | ||
+ | ====C++ 的编译 / 链接模型==== | ||
+ | 通常情况下,处理程序的方式有两种: | ||
+ | - 简单加工 | ||
+ | - 编译+链接 | ||
+ | ==简单加工模型== | ||
+ | 将所有的内容都堆在一块,直接进行编译: | ||
+ | {{ : | ||
+ | * 加工时间长 | ||
+ | * 少量修改也会导致全部重新加工 | ||
+ | ==分块处理== | ||
+ | {{ : | ||
+ | 每个文件单独编译,再进行链接 | ||
+ | * 编译耗费资源,但一次输入少 | ||
+ | * 链接输入多,但速度快 | ||
+ | * 便于升级(只需要修改需要的文件即可) | ||
+ | ===C++ 的编译 / 链接模型=== | ||
+ | C++ 基于分块处理的概念来定义自己的编译链接模型。由此概念引申出了几个重要的概念: | ||
+ | ==定义与声明== | ||
+ | * 变量的问题 | ||
+ | * 如果是简单加工,那么只需要定义一个变量即可 | ||
+ | * 如果是分块处理,那么多个文件中很可能都会使用到这个变量 | ||
+ | * 处理的办法是:**分离变量的定义与声明**,定义只有一处;在**需要使用的地方进行声明** | ||
+ | * 该定义会在**链接期**进行查找 | ||
+ | * 头文件与源文件 | ||
+ | * 按需声明的做法,在文件较多的情况下也比较费时费力 | ||
+ | * 解决的方法:**将所有的声明装进头文件**,在需要使用的地方**包含该头文件**即可 | ||
+ | * 编译器会将头文件自动展开 | ||
+ | * 翻译单元(编译器处理) | ||
+ | * 用于处理源文件和头文件的关系 | ||
+ | * 将某个源文件,以及相关的头文件,除开应该忽略的预处理语句,构造出来的东西。 | ||
+ | * 一处定义原则 | ||
+ | * 要求**所有**的翻译单元里只能有**一个定义**(因为编译器必须要看到定义才能编译) | ||
+ | * 程序级:函数 | ||
+ | * 翻译单元级:内联函数,类,模板 | ||
+ | ==编译链接模型== | ||
+ | * **预处理**(// | ||
+ | * 将源文件变为翻译单元( '' | ||
+ | * 防止头文件被循环展开:嵌套的头文件会在预处理过程中反复展开 | ||
+ | * 使用宏 ''# | ||
+ | * 缺点:宏重名可能导致引入失败 | ||
+ | * 使用 ''# | ||
+ | |||
+ | <code bash> | ||
+ | g++ -E ./main.cpp o ./main.i | ||
+ | </ | ||
+ | * **编译**(// | ||
+ | * 优化的缺点:可能使 debug 的信息丢失, 因此会将程序编译分为 realease 编译(速度)和 debug 编译(调试) | ||
+ | * 增量编译:单独修改某个文件后进行编译,也就是根据源文件的最新时间来判断 | ||
+ | * 如果修改了头文件,那么应该重新编译所有的源文件(某些老编译器不支持,此时需要全部编译) | ||
+ | * 全部编译(// | ||
+ | <code bash> | ||
+ | g++ main.i -S - o main.s | ||
+ | </ | ||
+ | * **汇编**(// | ||
+ | <code bash> | ||
+ | g++ main.s - c- o main.o | ||
+ | </ | ||
+ | * **链接**(// | ||
+ | * 整合所有的目标文件 | ||
+ | * 关联声明和定义 | ||
+ | * 生成可执行文件 | ||
+ | * 链接的种类: | ||
+ | * 内部链接:如果变量只能存在翻译单元里面,那么是内部链接 | ||
+ | * 外部链接:如果可以存在于翻译单元之间,那么是外部链接('' | ||
+ | * 无连接:都不可见,则无连接 | ||
+ | * 常见链接错误:定义不可见(比如有声明没定义的情况) | ||
+ | * 查看当前程序的外部链接:'' | ||