各种进制的表示

0B00001101 0xff 076

clangd消除警告的方法

  • 核心参考官方文档
  • 编写项目的.clangd文件,然后添加需要抑制的内容,这个内容可以根据提示获取
Diagnostics:
  Suppress: -Wc++17-extensions
  • 头文件检查的,在 .clang-tidy 添加
Checks: -misc-definitions-in-headersChecks: -misc-definitions-in-headers

clangd找不到头文件方法

cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1
  • 会生成compile_commands.json
  • clangd索引会生成.cache文件夹

clang-format格式化方法

  • 傻逼clangd-format必须制定-style=file才会根据.clang-format格式化,否则直接
  • vim文件可以参考https://raw.githubusercontent.com/llvm-mirror/clang/master/tools/clang-format/clang-format.py
" BEGIN CLANG-FORMAT CONFIG
" imap <C-K> <c-o> :pyf /home/zhuling/code_standards/format/clang-format.py<cr>
nmap <leader><leader>f :pyf /home/zhuling/code_standards/format/clang-format.py<cr>
function! Formatonsave()
  let g:clang_format_path = "/home/zhuling/code_standards/format/clang-format"
  let l:formatdiff = 1 " 保存文件时针对改动的代码进行格式化,历史代码不动
  "let l:lines="all" "如果保存文件时也希望对历史代码做格式化,可取消该行的注释
  pyf /home/zhuling/code_standards/format/clang-format.py
endfunction
autocmd BufWritePre *.h,*.cc,*.cpp call Formatonsave()
" END CLANG-FORMAT CONFIG

compile_commands.json生成方法

  • cmake,apt install cmake

cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1

  • bear,apt install bear

如何编译的在前面加上bear --,类似bear -- make,bear make

  • clang,apt install clang

clang -MJ compile_commands.json source.cpp

malloc

C++多态的多种实现

  1. 重载。函数重载和运算符重载,编译期。
  2. 虚函数。子类的多态性,运行期。 在继承关系中,对于父类的方法我们也同样使用。但是正常来说,我们希望方法的行为取决于调用方法的对象,而不是指针或引用指向的对象有关。
  3. 模板,类模板,函数模板。编译期

智能指针

weak_ptr

  • weak_ptr<T> 模板类中没有重载 * 和 -> 运算符,这也就意味着,weak_ptr 类型指针只能访问所指的堆内存,而无法修改它。
  • weak_ptr主要针对shared_ptr的空悬指针和循环引用问题
  • 空悬指针问题:有两个指针p1和p2,指向堆上的同一个对象Object,p1和p2位于不同的线程中。假设线程A通过p1指针将对象销毁了(尽管把p1置为了NULL),那p2就成了空悬指针。
  • weak_ptr不控制对象的生命期,但是它知道对象是否还活着。如果对象还活着,那么它可以提升为有效的shared_ptr(提升操作通过lock()函数获取所管理对象的强引用指针);如果对象已经死了,提升会失败,返回一个空的shared_ptr
  • 当 weak_ptr 类型指针的指向和某一 shared_ptr 指针相同时,weak_ptr 指针并不会使所指堆内存的引用计数加 1;同样,当 weak_ptr 指针被释放时,之前所指堆内存的引用计数也不会因此而减 1。也就是说,weak_ptr 类型指针并不会影响所指堆内存空间的引用计数

static作用

  1. 如果用 static 关键字修饰的话,该变量便会存 放在静态数据区,其生命周期会一直延续到整个程序执行结束
  2. 用 static 对全局变量进行修饰改变了其 作用域范围,由原来的整个工程可⻅变成了本文件可⻅
  3. 用 static 修饰函数,情况和修饰全局变量类似,也是改变了函数的作用 域
  4. 对类中的某个函数用 static 修饰,则表示该函数属于一个类而 不是属于此类的任何特定对象;如果对类中的某个变量进行 static 修饰,则表示该变量以及所 有的对象所有,存储空间中只存在一个副本,可以通过;类和对象去调用。

强制转换

dynamic_cast

  • 用于指针或者引用父类转子类,如果转失败返回NULL

static_cast

  • 和C的强制转换差不多

const_cast

  • 用于去除和加上const

reinterpret_cast

  • 完成任意指针类型向任意指针类型的转换

others

  1. string的substring函数第二个参数是len长度而不是pos

push_back和emplace_back区别

  1. push_back 接受一个对象,将其拷贝(或移动)到容器中,而 emplace_back 接受一组参数,直接在容器中构造对象。
  2. push_back 调用对象的拷贝构造函数(或移动构造函数),将对象拷贝(或移动)到容器中,而 emplace_back 调用对象的构造函数,在容器中直接构造对象
  3. 使用 emplace_back 比 push_back 更高效,因为它避免了对象的拷贝(或移动)操作,直接在容器中构造对象。此外,emplace_back 还可以支持可变参数,可以方便地构造带有多个参数的对象。
  4. emplace_back 的参数必须和对象的构造函数参数匹配,否则会编译错误。而 push_back 接受的是一个对象,因此可以隐式转换类型

编译顺序

[!tip] 编译方式百花齐放,最多的就两种,一种是makefile,一种是cmake

预处理

  • 将所有一起编译的.c文件合并
  • 将宏展开计算,将注释以及删除无用字符
  • 将头文件内容插入到源文件(因为#include也是宏,本质还是宏展开),头文件路径的添加可以通过-I
  • 通过这个步骤的带一个文件包含了精简化后的所有的源代码(当然还是不包括库的)

编译

  • 这一步将上一步得到的源代码进行编译成为汇编代码
  • 这一步只会检查代码的正确性,不会检测所需要的函数是否有实现,如果函数没有定义,但是引用了会在这里报错,但是定义了,使用了,没有实现,这里还不会报错

汇编

  • 汇编器会将汇编代码转换成二进制指令,同时生成与指令相关的符号表和重定位表

链接

  • 这一步非常重要,链接器会解析所有目标文件中的符号,包括函数名、变量名等,并为每个符号分配一个唯一的地址。由于不同目标文件中的符号地址可能会相互引用,链接器需要对这些符号进行重定位,即将符号的地址修改为正确的地址。链接器还会将程序所依赖的库文件链接到可执行文件中,以便程序在运行时可以调用库函数。
  • 定义了使用了没有实现的函数会在这里被发现,包括引用了外部的.a静态库但是没有通过-l加入也是在这里发现,一般是白色的undefine的错误,链接器会检查每个跳转的是否ok
  • 这一步中如果需要加入.a(linux)或者.lib(win)的路径,可以通过-L,对应-l通常就是静态库的名字

右值引用

类型

  • 左值指存储在内存中、有明确存储地址(可取地址)的数据;
  • 右值是指可以提供数据值的数据(不可取地址);右值又分为纯右值(prvaule),亡值(xvalue),纯右值指的是类似1,2这种纯数字,这种不能纯数字不能被修改,亡值指的是类似函数返回值,函数参数,临时变量这种临时构建,非引用返回的临时变量、运算表达式产生的临时变量、原始字面量和 lambda 表达式,这部分可以被右值引用修改
  • 右值引用可以延长生命周期如下面,可以减少临时对象的构建
int&& value = 520;
class Test{
public:
    Test(){
        cout << "construct: my name is jerry" << endl;
    }
    Test(const Test& a){
        cout << "copy construct: my name is tom" << endl;
    }
};
Test getObj(){
    return Test();
}
int main(){
    int a1;
    int &&a2 = a1;        // error
    Test& t = getObj();   // error
    Test && t = getObj();
    const Test& t = getObj();
    return 0;
}
  • 可以通过移动构造函数减少不必要的构造

std::move

  • 将数据转化为右值,标记对象为临时对象
  • 本身上并没有什么效率提升,仅仅只是一个转换为右值的功能,但是因为可以转换为右值,可以调用右值的构造函数,右值的构造又可以避免对象的拷贝(虽然左值也可以实现,但是左值更多是copy而不是move,因此会有const,右值就没有const),具体参考

::运算符

  • 表示使用某个命名空间下的操作
  • 如果前面没有命名空间表示全局的意思,全局的命名空间

宏中使用do{}while(0)原因

  • 使用do{...}while(0)构造后的宏定义不会受到大括号、分号等的影响,总是会按你期望的方式调用运行

如果仅仅使用func1();func2(); 遇到使用if加单行语句就会出问题

构造函数=default的作用

  1. 用于标记使用默认的构造函数,如果有的构造函数和编译器默认生成的一样,字节使用=default(特别是拷贝构造函数)

C++代码计算运行时间

int main() {
    // "start" and "end"'s type is std::chrono::time_point
    time_point<system_clock> start = system_clock::now();
    {
        std::this_thread::sleep_for(std::chrono::seconds(2));
    }
    time_point<system_clock> end = system_clock::now();

    std::chrono::duration<double> elapsed = end - start;
    std::cout << "Elapsed time: " << elapsed.count() << "s";

    return 0;
}

奇形怪状的错误

type nofind 错误

  • 一定需要考虑是不是头文件重复包含的问题 A头文件和B头文件都include了对方,导致顺序出问题了,可以考虑用extern 声明避免nofound

 the vtable symbol may be undefined because the class is missing its key function

  • 类中的第一个虚函数没有实现,导致虚函数表没有办法实现

warning:#pragma once in main file

  • 不要编译头文件,带着头文件编译就会有这个错误,确保g++的命令接的全是.cpp的文件

multiple definition of

  • 核心是因为头文件中包含了实现(理论上应该放到cpp文件中)
  • 因为C++编译器是按照 .cpp文件变异成为.o文件,如果多个cpp引用同一个头文件,每个.o文件都有一份实现,最后.o链接的时候就会出问题
  • 但是下面几种情况是例外的
    • 内联函数的定义
      • 因为内联函数直接被替换了, 不会出现函数的概念
    • class/struct/union 定义的函数
      • 类里面的函数实现如果放在头文件中默认变成内联的,直接替换不会出问题
    • const 和 static 变量
      • static限制了变量的作用域,该变量仅在引用.h的源文件中有效,也就是说.h被引用了几次这个变量就被定义了几次,且各变量之间互不影响(各变量具有不同的内存地址)。这种方法不适用于定义全局变量,因为它们不是同一个变量。
      • const 默认就是static类型的变量

[!tip] 参考 zhuanlan.zhihu.com/p/577994847 注意头文件规则,避免链接错误:重复定义(multiple defination) - bw_0927 - 博客园 C++ multiple definition 总结 - 简书

socket broken pipe问题

  • 对已经关闭的socket 执行二次写的时候会出现, 可以通过 signal(SIGPIPE, SIG_IGN); 解决

[!tip] 参考 socket编程—— 服务器遇到Broken Pipe崩溃 - 静之深 - 博客园

如何给random函数设置随机的seed

 #ifdef __i386
  __inline__ uint64_t rdtsc()
  {
      uint64_t x;
      __asm__ volatile ("rdtsc" : "=A" (x));
      return x;
  }
  #elif __amd64
  __inline__ uint64_t rdtsc()
  {
      uint64_t a, d;
      __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
      return (d<<32) | a;
  }
  #endif
  • 使用嵌入式汇编来获取CPU的时间戳计数器(TSC)的值。TSC是一个递增的64位计数器,它记录了从电源启动或者最近一次复位以来的时钟周期数。64位架构中,由于TSC的值超过了32位寄存器的容量,所以需要使用两个32位寄存器来存储TSC的高位和低位,并将它们进行位移和合并操作,最后返回一个完整的64位值

sizeof和strlen区别

  • sizeof计算char* 类型时候计算的是指针的长度,而不是字符串长度,strlen则看作是字符串,但是字符串不计算\0,因为\0被视为字符串结束的位置
  • sizeof计算char[]类似的时候计算的是字符串长度,而且包括\0

C++协程实现

  • 底层还是通过linux提供的API makecontext实现的,实现可以参考

multiple definition of问题

  • 不要把全局变量以及全局方法的定义放在头文件里!!!!,这样就会出错
  • 核心是因为编译器编译分开每一个cpp的时候直接他妈的展开了所有的头文件,放在一起编译就不会有问题
  • 但是class的静态函数就没问题

spdlog的问题

显示一大堆nodefine的问题

[!tip] 参考 https://blog.csdn.net/CSSDCC/article/details/121854773 https://fmt.dev/latest/usage.html#usage-with-cmake

  • 可以直接在makefile中添加-lfmt手动链接,makefile

代码规范

核心参考Google C++规范 #代码规范

  • 注意头文件和对应的源文件实现要放在同一个文件夹中, 不要用愚蠢的include文件夹

命名

  • 枚举enum的命名 应当和常量 或宏 一致: kEnumName 或是 ENUM_NAME.
  • 宏的命名不要以下划线开头,因为这是C语言标识符保留的
  • 变量使用下划线命名法+小写
  • 函数和类使用驼峰法+首字母大写
  • 命名空间使用全小写,不要出现下划线
  • 类内部属性使用下划线命名法+下划线的后缀player_pos_

Class

  • 类应该public放在前面(因为这是直接暴露给使用者的),private放在后面
  • set值方法set_pos(Pos pos)使用 set+属性名字 小写
  • get值方法直接使用pos()使用小写名字
  • 类内部属性使用下划线命名法+下划线的后缀player_pos_

项目组织规范

  • 参考 GitHub - chenxuan520/cppnet: Lightweight C++ network library
  • 第三方引用的仓库, 如果是编译类型的, 通过 submodule 添加到 third_party , 然后编译时候一起编译, 如果是二进制类型的, 通过脚本进行下载引入即可, 不要直接放到仓库中
  • hpp 和对应的 cpp 放在同一文件夹
  • test 文件夹是必要的, 作为单元测试存在

错误标识

  1. 通过返回RC,然后设置RC到string的转移,大多数使用这种办法
    • 麻烦,但是标准程度高
  2. 返回特定的Err结构体,类值go的error,
    • 标准,但是通用型差
  3. 返回int标识类型,参数中携带std::string& err_msg标识错误信息,微信支付方法
    • 通常小于0标识系统错误,大于0标识逻辑错误

有用的宏

[!tip] 这些宏必须经两层包装才能被使用

__LINE__ :当前行数
__func__ :当前函数名
__FILE__ :当前文件名
__COUNTER__ :计数器,会随着调用递增

Lambda 表达式捕获

  • 默认是使用= 捕捉所有的值拷贝,使用 & 捕捉所有的引用拷贝
  • 如果使用 [x] 默认只捕获这个值拷贝, [&x] 默认只捕获这个引用拷贝

内存泄漏检查工具 valgrind

  • 安装 apt install valgrind
  • 使用 valgrind --tool=memcheck --leak-check=full <二进制文件>
  • 注意 这个和cmake > 内存泄漏检查sanitize 冲突不要一起使用

参考(重要)