现代C++白皮书

Read more →

C++ Primer Plus

编译器 # 把高级语言编译成可执行语言工具,分为前端后后端,前端值得是高级语言的解析,后端是指翻译解析之后的结果为机器语言 多文件 ** 连接 多文件编译可以有两种方式,一是直接编译为一个可以执行文件,二是按模块或者按文件编译为库,然后连接到执行文件 连接方式有两种, 一是静态连接,把所有的库文件打包到最后的生成文件中,优点是不需要额外的依赖外部环境,独立性强,缺点是文件体积大 二是动态链接,为了解决静态链接的缺点,执行文件在执行到库相关的代码的时候才加载库,有一点需要注意的是,程序运行的时候,在使用到动态库的时候才映射动态库到内存空间中。原理是编译待援在编译的时候,会更具声明生成函数的调用逻辑,但是只是一个地址跳转语句,所以,只要不调用,就不会有问题,当调用到了。才会加载库然后映射库的地址,这个完整的过程称为重定向。 动态连接 C语言编程透视 声明 声明是为了在编译的时候编译器能进行完整的上下文编译。他需要更具声明来确定编译信息,否则编译器无法确定编译中的语句信息,声明可以辅助完成这个情况, 所以理论上编译的时候是可以不需要实现的,可以在其他编译单元中实现声明的函数,其声明的文件可以不引用头文件,即两个编译单元完全可以无任何联系,除了声明之外,在连接的时候,连接器会根据编译出来的信息去确定函数调用情况,这里有一个问题,按上述的描述,是一个声明对应一个实现,如果有一个声明对应多个实现呢 == : 会有覆盖问题,如果多个动态链接库都有同一个声明的实现,则连接的时候连接第一个,后面的则忽略,这也提醒我们,在大型项目中,避免同名全局函数或者变量,使用namespace或者static限制作用域, g++ -o tt ../main.cpp -ldl ./libhellolib.so ./libhellolib1.so LD_LIBRARY_PATH=$PWD ./tt 头文件,避免公用代码的重复,预处理时展开头文件,需要使用#pragma once避免重复引用,头文件只是简单的文件替换,理论上的可以替换任何文本。 cmake * 子模块,使用add_subdirectory引入 * 第三方库 * 只是头文件,直接指定头文件目录编译即可 * 使用子模块 * 使用为连接库 * 使用git模块 STL 重点为容器和算法 lambda表达式,实质上是仿函数,是一个结构体实现()运算的重载,捕获的时候按照声明的新式捕获参数,建议使用的时候明确使用的参数,使用哪个就捕获哪一个,否则他实际上会占据一定的大小的,配合std::function使用 CTAD — complie-time argument deduction,编译器参数推断,C++17引入的,可以在编译器按照上下文推断类型,具体表现在lambda参数可以使用auto,容器可以不适用<> ranges https://zhuanlan.zhihu.com/p/350068132 module raii 获取资源即初始化,释放资源即销毁,具体的实现是使用构造函数和析构函数,当前的实现为智能指针,其他用户自己管理的资源最好也使用raii,遮这样在函数有多个出口的时候,就不会有意外的情况,本质上还是自己管理资源,不想其他的语言有GC 异常安全,C++中异常机制在回溯栈的时候会析构对象,所以如果没有实现RAII,则自己管理的内存则无法释放,C++的异常可以发生在任何地方,如果发生在析构函数中,则需要自己处理,不要在析构函数中抛出异常的,在构造函数中的时候,也需要捕获异常然后释放已经申请的资源,构造函数异常的时候,是不会调用析构函数的, 构造函数 构造函数有时候会隐士的生成对象,即使没有显示声明,使用explicit避免这种情况,单参数的时候会,多参数使用{},调用的时候也会 直接使用多参数的时候,()和{}是有区别的,()除了正常的使用外,其他情况不具备特殊含义 int a = (10, 11); int a = {10, 11}; tt t(1, 2); tt q{1, 3}; tt w = {1, 4}; tests({1,5}); ``` 上面的语句1正确,a的值为11,2错误,{}这种用法的意义是参数列表,是会构造对象的。ps,调用构造函数的时候,有具体的对象的时候两种括号无差别,但是无对象的时候有区别,如上,细品 默认构造函数 拷贝构造( A(A const & a) ) A a = aa; 移动构造( A(A && a) ) 赋值构造( A&operator=(A const& a) ) A a; a = aa; 移动赋值( A&operator=(A && a) ) =delte和=default 类内部变量可以赋初值 三五法则 拷贝构造或者赋值构造需要区分深拷贝和浅拷贝,这也是构造函数肯可能引入的问题,例如浅拷贝导致内存的重复释放, 各种构造函数更多的是需要考虑当前对象的来源,如果是直接从零开始的,则是普通的构造函数,如果是从别的对象来的,则需要考虑深浅拷贝的问题,以及构造之后别的对象是否还需要的问题,简而言之,就是资源细节上的考虑,只要内涉及到资源的操作,则需要多话费一些心思区考虑, 函数返回多值
Read more →