title: “ 《Linux多线程服务端编程:使用muduo C++网络库》笔记(2)\t\t”
tags:

  • 笔记
    categories:
  • C/C++
  • 后端
    date: 2019-02-25 13:51:24

C++编译链接模型

  • C++为了完全兼容C,包括C的编译/内存模型等,继续使用单遍编译,要求前向声明,要使用include等,导致编译缓慢,且在重载、虚函数等方面实现复杂
  • include将引用文件替换至当前位置,会导致代码量增多,同时include的顺序可能导致实际结果不一样
  • 前向声明可能导致函数位置互换后结果不一样
  • 头文件使用规则:
  1. 文件之间依赖尽量最小、定义式之间依赖尽量最小,避免循环依赖
  2. 让class的名称、头文件、源文件名具有相关性
  3. 令头文件自给自足
  4. 头文件内写宏,防止重复编译。#ifndef #define? #endif
  5. 如果编写程序库,公开的文件应表达模块的接口
  • 动态库的更新会影响调用者程序,会改变其他软件的功能。需要让多个大版本的动态库能够共存以保证所有调用者的独立性
  • 静态库的编译会在软件编译前,两者编译时的依赖库版本可能不同,会导致编译错误、不可预期的运行时错误。需要保证静态库与程序编译时的环境一致。
  • 静态库演化需要在底层库新增变体后,所有依赖它的高层库/程序都为之编译一个版本,多种组合方式会导致版本爆炸式增长:cab1.0net1.2_boost1.40_gcc44……
  • 静态/动态库都可能因为C++头文件与源文件分离的特性出现发布的库文件与头文件不一致的情况
  • 尽量用源码编译,虽然比调用库编译时间长,让build工具能够自动check out库的源码。如gyp、typhoon-blade、CMake、SCons等工具

C++面向对象与虚函数

  • 多组合少继承,不符合A is B就不继承。代码应以实用、朴实、好用、易懂为目标编写,而不是为了实用C++各种编程范式和特性而写
  • 软件开发的首要技术使命是控制复杂度,不要因为某种技术流行就直接使用,除非能降低复杂度
  • 设计模式是常用问题的解决方案,也是绕过C++语言限制的技巧
  • C/C++二进制兼容性,涉及到库的更新需要考虑。library的ABI,主要包括:
  1. 函数参数传递方式
  2. 虚函数的调用方式
  3. struct/class的内存布局,偏移量访问数据成员
  4. name mangling(重载的重命名算法)
  5. RTTI(运行时类型信息识别,多态用)和异常处理实现
  • 源码兼容,二进制不兼容的例子:
  1. 给函数增加默认参数
  2. 增加虚函数,会导致vtbl排列变化
  3. 增加默认模板类型参数会改变name mangling
  4. 改变enum值,增加class数据成员也会导致sizeof(class)变大
  • 解决方案:采用静态连接(不是静态库,是从源码编译)、为动态库增加版本管理、pimpl(头文件只暴露非虚接口并且class大小固定这样可以随意更新库文件,但会多一层间接性)
  • 基于闭包的编程:继承和多态可用std::function,std::bind来实现(闭包closure,可以将一个function对象绑定到任意对象的函数/函数上),包括设计模式中的接口,取代虚函数
  • 如果使用stdio,int64_t在32 64平台不是相同类型,需要包含inttypes.h并使用PRId64宏,如果使用iostream不需要考虑,但虚继承等效率低下。
  • iostream不是线程安全的,多个<< <<不保证原子性。不建议使用iostream,可以用stdio或者自己写针对性的操作函数

C++经验谈

  • 用异或交换变量是错误的,效率并没有增高,甚至低效,也降低了易读性
  • 不要重载全局::operator new(),去性能优化、检测内存错误、统计内存使用情况,这样第三方库无法保证同样的分配器。应替换malloc(),free()的实现
  • 除法取整有两种模式,向0取整,向负无穷取整,C++11/Java为向0,解释型语言部分是向负无穷
  • 单元测试mock可以使用设计时增加接口以多态实现(增加了虚影响效率)、编译期或链接期迟邦定-链接期垫片(通过namespace封装一套接口,利用namespace不封闭特性,可在不改变源文件情况下改动namespace的内容)
  • 慎用匿名namespace:匿名空间中的函数也是匿名的,需要引用的时候比较麻烦(调试时候,两个文件的匿名namespace中有相同名字的函数,无法区分是哪个);某些g++同一个文件的编译后二进制文件不同,可能是build tool失灵
  • 采用有利于版本管理的代码格式:合理使用换行符、多行注释用//(diff中可清晰看到大段注释的变化)、一行代码只定义一个变量、如果参数大于3个每个逗号后换行、class初始化列表也一行一个、namespace不缩进、避免使用版本控制软件的keyword substitution功能
  • 用对grep友好的代码风格(linux正则搜索命令):操作符不重载+-符号、用C++风格的cast进行类型转换