OWenT's blog
  • Introduction
  • About Me
  • 2020
    • 近期对libatapp的一些优化调整(增加服务发现和连接管理,支持yaml等)
    • xresloader转表工具链增加了一些新功能(map,oneof支持,输出矩阵,基于模板引擎的加载代码生成等)
    • 在游戏服务器中使用分布式事务
    • libcopp接入C++20 Coroutine和一些过渡期的设计
    • libatbus 的大幅优化
    • nftables初体验
    • 容器配置开发环境小计
  • 2019
    • PALM Tree - 适合多核并发架构的B+树 - 论文阅读小记
    • 跨平台协程库 - libcopp 简介
    • C++20 Coroutine 性能测试 (附带和libcopp/libco/libgo/goroutine/linux ucontext对比)
    • 尝鲜Github Action
    • 一些xresloader(转表工具)的改进
    • protobuf、flatbuffer、msgpack 针对小数据包的简单对比
    • 协程框架(libcopp) 小幅优化
    • Excel转表工具(xresloader) 增加protobuf插件功能和集成 UnrealEngine 支持
    • Anna(支持任意扩展和超高性能的KV数据库系统)阅读笔记
    • C++20 Coroutine
    • libcopp merge boost.context 1.69.0
    • Google去中心化分布式系统论文三件套(Percolator、Spanner、F1)读后感
    • Rust玩具-企业微信机器人通用服务
  • 2018
    • 使用ELK辅助监控开发测试环境服务质量和问题定位
    • Webpack+vue+boostrap+ejs构建Web版GM工具
    • 2018年的新通用伪随机数算法(xoshiro / xoroshiro)的C++(head only)实现
    • Rust的第二次接触-写个小服务器程序
    • 理解和适配AEAD加密套件
    • atsf4g-co的进化:协程框架v2、对象路由系统和一些其他细节优化
    • 协程框架(libcopp)v2优化、自适应栈池和同类库的Benchmark对比
    • 可执行文件压缩
    • 初识Rust
    • 使用restructedtext编写xresloader文档
    • atframework的etcd模块化重构
    • C++的backtrace
  • 2017
    • ECDH椭圆双曲线(比DH快10倍的密钥交换)算法简介和封装
    • protobuf-net的动态Message实现
    • pbc的proto3接入
    • atgateway内置协议流程优化-加密、算法协商和ECDH
    • 整理一波软件源镜像同步工具+DevOps工具
    • Blog切换到Hugo
    • libcopp v2的第一波优化完成
    • libcopp(v2) vs goroutine性能测试
    • libcopp的线程安全、栈池和merge boost.context 1.64.0
    • GCC 7和LLVM+Clang+libc++abi 4.0的构建脚本
    • libatbus的几个藏得很深的bug
    • 用cmake交叉编译到iOS和Android
    • 开源项目得一些小维护
    • atapp的c binding和c#适配
    • 对象路由系统设计
    • 2016年总结
    • 近期的一个协程流程BUG
  • 2016
    • 重写了llvm+clang+libc++和libc++abi的构建脚本
    • atsf4g完整游戏工程示例
    • atframework基本框架已经完成
    • 游戏服务器的不停服更新
    • 对atbus的小数据包的优化
    • Android和IOS的TLS问题
    • pbc的一个陈年老BUG
    • boost.context-1.61版本的设计模型变化
    • 接入letsencrypt+全面启用HTTP/2
    • 理解Raft算法
    • libatbus基本功能及单元测试终于写完啦
    • 博客文章和文档迁移到gitbook
  • 2015
    • 博客文章和文档迁移到gitbook
    • 给客户端写得LRU缓存
    • 近期活动比较零散
    • 关于BUS通信系统的一些思考(三)
    • 针对Java JIT的优化(转表工具:xresloader)
    • libcopp更新 (merge boost 1.59 context)
    • 小记最近踩得两个C++坑
    • Redis全异步(HA)Driver设计稿
    • Vim常用命令
    • 关于firewalld和systemd的一些命令速记
    • Jenkins(hudson)插件记录
    • 我们的Lua类绑定机制
    • LLVM+Clang+Libcxx+Libcxxabi(3.6)工具链编译(完成自举编译)
    • 回顾2014
    • Android NDK undefined reference to ___tls_get_addr 错误
    • gitlab腾讯企业邮箱配置
  • 2014
    • 回顾2013
    • C++11动态模板参数和type_traits
    • C++又一坑:动态链接库中的全局变量
    • tolua++内存释放坑
    • [转]类似github的框架
    • Lua性能分析
    • 集成Qt Webkit 到cocos2d-x
    • Gitlab环境搭建小计
    • 近期研究VPN的一些记录(OpenVPN,pptp,l2tp)
    • LLVM + Clang + Libcxx + Libcxxabi 工具链编译
    • 关于BUS通信系统的一些思考(二)
    • 关于BUS通信系统的一些思考(一)
    • [libiniloader] Project
    • 记录一些在线编辑器
    • [WP Code Highlight.js] Project
    • 再议 C++ 11 Lambda表达式
    • 基于Chrome插件的开发工具链
    • [ACM] HDU 1006 解题报告
    • Linux 编译安装 GCC 4.9
    • 又碰到了这个解谜游戏,顺带记下地址
    • 简单C++单元测试框架(支持一键切到GTest或Boost.Test)
    • 捣鼓一个协程库
  • 2013
    • std和boost的function与bind实现剖析
    • 不知道是哪一年的腾讯马拉松题目 照片评级 解题报告
    • Lua 挺好用的样子
    • VC和GCC成员函数指针实现的研究(三)
    • VC和GCC成员函数指针实现的研究(二)
    • VC和GCC内成员函数指针实现的研究(一)
    • 一个C++关于成员变量偏移地址的小Trick
    • ptmalloc,tcmalloc和jemalloc内存分配策略研究
    • POJ 2192 Zipper HDU 2059 龟兔赛跑
    • 从Javascript到Typescript到Node.js
    • 网络编程小结
    • 试试Boost.Asio
    • Lnmp yum 安装脚本 (for CentOS)
    • ARM 交叉编译环境搭建
    • Linux 编译安装 GCC 4.8
    • [记录]虚拟硬盘的压缩|磁盘写零
  • 2012
    • Boost.Spirit 初体验
    • “C++的90个坑”-阅读笔记
    • AC自动机
    • C++ 标准过渡期
    • 程序员修炼之道 -- 阅读笔记
    • [转载]狼与哈士奇
    • C++ 新特性学习(八) — 原子操作和多线程库[多工内存模型]
    • C++ 新特性学习(七) — 右值引用
    • 理解Protobuf的数据编码规则
    • 忆往昔ECUST的ACM时代
    • Linux编译安装GCC 4.7
    • JSON显示库 -- showJson (Javascript)
    • C++ 新特性学习(六) — 新的字符串编码和伪随机数
    • C++ 新特性学习(五) — 引用包装、元编程的类型属性和计算函数对象返回类型
    • C++ 新特性学习(四) — Bind和Function
  • 2011
    • C++ 新特性学习(三) — Regex库
    • C++ 新特性学习(二) -- Array、Tuple和Hash库
    • C++ 新特性学习(一) -- 概述+智能指针(smart_ptr)
    • Linux 和 Windows PowerShell 常用工具/命令 记录
    • 非常帅气的Linq to sql
    • 2011 Google Code Jam 小记
    • C++总是很神奇
    • 大学生创新项目[国家级]经费使用记录
    • 常用官方文档整理
    • 我们学校的IPV6很不错嘛
  • 2010
    • 线段树相关问题 (引用 PKU POJ题目) 整理
    • 2010 ACM 赛前笔记
    • POJ PKU 2596 Dice Stacking 解题报告
    • POJ PKU 3631 Cuckoo Hashing 解题报告
    • POJ PKU 1065 Wooden Sticks 3636 Nested Dolls 解题报告
    • HDU 3336 Count the string 解题报告
    • Hash模板 个人模板
    • ZOJ 3309 Search New Posts 解题报告
    • POJ PKU Let's Go to the Movies 解题报告
    • 注册表常用键值意义
    • PKU POJ 1724 ROADS 解题报告
    • 《神奇古今秘方集锦》&《民间秘术大全》
    • PKU POJ 1720 SQUARES 解题报告
    • POJ PKU 2155 Matrix 解题报告
    • PKU POJ 1141 Brackets Sequence 解题报告
    • PKU POJ 2728 Desert King 解题报告
    • PKU POJ 2976 Dropping tests 解题报告
    • PKU POJ 3757 Simple Distributed storage system 解题报告
    • GCD Determinant 解题报告
    • Southeastern European 2008 Sky Code 解题报告
    • HDU HDOJ 3400 Line belt 解题报告
    • 线性筛法求质数(素数)表 及其原理
    • HDU HDOJ 3398 String 解题报告
    • 树状数组模块(个人模板)
    • 浙江理工 省赛总结 team62 By OWenT of Coeus
    • POJ PKU 3659 Cell Phone Network 解题报告
    • USACO 2008 March Gold Cow Jogging 解题报告
    • C#格式化输出(记录)
    • 参加有道难题笔记
    • POJ PKU 2446 Chessboard 解题报告
    • POJ PKU 1986 Distance Queries 解题报告
    • 计算几何算法概览[转载]
    • 关于差分约束(转载)
    • POJ PKU 2826 An Easy Problem?! 解题报告
    • 数论模板(个人模板)
    • 简易四则运算(ACM个人模板)
    • Catalan 数
    • The 35th ACM/ICPC Asia Regional Tianjin Site —— Online Contest 1009 Convex 解题报告
    • JQuery扩展插件--提示信息
    • ACM 计算几何 个人模板
    • 解析网站字符串型参数 Javascript QueryString 操作 TQueryString类
    • POJ PKU 1474 Video Surveillance 解题报告
  • 2009
    • 模式匹配(kmp)个人模板
    • 并查集 模板
    • POJ 3267 The Cow Lexicon 解题报告
    • C/C++语言常用排序算法
    • POJ 2606 Rabbit hunt 2780 Linearity 1118 Lining Up 解题报告
    • 打造最快的Hash表(转) [以暴雪的游戏的Hash为例]
    • ECUST 09年 校赛个人赛第六,七场总结
    • ECUST 09年 校赛个人赛第三场部分解题报告(A,D,F,I)
    • 牛顿迭代解方程 ax^3+bX^2+cx+d=0
    • 09年8月9日 ECUST ACM 练习赛总结
    • 连接最多点直线 (OWenT 个人模板)
    • 点到直线距离 和 线段间最短距离 (OWenT 模板)
    • ECUST 09年 校赛个人训练赛第五场总结
    • ECUST 09年 校赛个人赛第八场(最后一场)总结
    • 09年8月14日 ECUST ACM 练习赛总结
    • 矩阵相关 (增强中)
    • Prime最小生成树(个人模板)
    • 最长单调子序列 复杂度nlog(n)
    • POJ PKU 2549 Sumsets 解题报告
    • POJ PKU 3277 City Horizon 解题报告
    • 我的ACM生涯
    • POJ PKU 2528 Mayor's posters 解题报告
    • POJ PKU 2378 Tree Cutting 解题报告
    • POJ PKU 1990 MooFest 解题报告
Powered by GitBook
On this page
  • 前言
  • 总结

Was this helpful?

  1. 2012

“C++的90个坑”-阅读笔记

PreviousBoost.Spirit 初体验NextAC自动机

Last updated 6 years ago

Was this helpful?

前言

C++确实是一门复杂的语言。包括之前查看了一些C++11的文档和做了一些实践和总结,越来越觉得C++是门神奇的语言,也是个陷阱多多的语言。 我现在开发过程中最主要使用的语言就是C++,所以了解C++的一些细节和问题非常重要,后来看到某大神的一篇文章,激起了我专门去看一看关于C++的一些常见的设计方法和问题的书。就是刚才提到的文章里有说的《Effecitve C++》和《More Effecitve C++》 共90个条款,所以说是90个坑。

因为只是阅读笔记,只是为了便于回忆,我就只记录了一些我觉得重要和我先前不知道或者没注意到的点:

Let's begin:

  1. 尽量以const、enum、inline代替#define,特别是类内部enum的运用

  2. 尽可能用const,我发现在实际写代码中者这可以让编译器帮你解决很多不经意的问题

  3. 全局对象的初始化顺序是不确定的,所以建议全局变量互相引用的时候,采用static局部变量的方式。static变量将会在第一次调用时初始化

  4. 请使用virtual析构函数,在A* p = new B时,如果A的析构函数不是virtual的,delete p会导致内存泄露等行为

  5. 不要让异常离开析构函数,原因也是容易造成泄露

  6. operator=或其他类似行为的函数要注意自我赋值的情况,即 stData = stData

  7. 在不同编译器中,对函数调用的参数执行顺序是不同的,要注意这一点(如: func(a(), b()),有些环境的执行顺序是a->b->func,有的是b->a->func

  8. shared_ptr和auto_ptr可以让动态链接库产生的对象在产生的模块中销毁,但是带来的问题是该智能指针无法升级

  9. 大部分情况下可以用pass-by-reference-to-const代替pass-by-value,但是在对象小的时候,比如char、bool、int,这么做完全没有必要

  10. 尽量把封装部分声明为private,因为暴露给外界的越多,表示你可以改动的地方越少

  11. 隐式类型转换只会分析一层,所以如果所有参数都需要隐式类型转换,建议使用non-friend、non-member函数

  12. 一个不抛异常的swap函数可减少很多互斥操作,也能有效减少错误处理的代码。特别要注意,一些STL库,如vector和dequeue在T的拷贝和赋值构造是异常安全的情况下才保证异常安全,这意味着vector和vector >都不是异常安全的

  13. 转型动作有时候很耗CPU,特别是dynamic_cast

  14. 继承的非virtual函数在重载之后会发生父类函数的覆盖,这时候可以手动using进来

  15. 纯虚析构函数必须有一个实现体,即便内容是空,否则会导致父类数据成员的内存泄漏

  16. private继承和has-a关系的区别是private继承可以减少内存占用,因为大多数编译器在申明内容为空的成员时都会给予一个字节,然后由于内存结构对齐,会扩充到4个(32位系统)或更多字节,最终可能导致一个数据结构的大小不能被CPU缓存。但是仍然不推荐使用private继承

  17. 大量使用模板可能会导致代码长度剧增,而最终导致代码缓存命中率下降

  18. *将与参数类型无关的参数抽离template,书上说会导致代码爆炸式增长,事实上,现在的编译器会优化掉,基本可以无视之

  19. 尝试用traits classes表现类型信息,具体可以看boost的traits库+配合static_assert无敌了

  20. new与delete重载和placement new与placement delete,还有操作失败时的handle函数,这些个东西着实不是一两句话可以描述清的,还是看书去吧

    new操作的正常语序是 1. operator new(size_t) // 分配内存 2. 调用构造函数 3. return 指针地址

    但是编译优化的时候有时候会把1和3合并了,这是在多线程编程时需要注意的地方

  21. 尽量不要重载&&、||和,因为无法达成和编译器一样的行为,比如:在 if ( a && b ) …中,如果a为false,b应该不执行,而如果a和b不是内置类型并且用户重载了&&符号会发生什么事?答案是会执行a.operator&&(b),结果显而易见,是先执行了b,然后执行&&操作符函数。这样就无法达到我们一般的思维。||和,操作符也类似,我们无法模拟出操作编译器的默认行为,所以如果不是我们另有语义上的目的或者我们能确保使用者能正确使用,不要重载这些操作符

  22. 使用包装器维护对象,使用析构函数释放对象,基本是最简单的防止异常抛出时的内存泄漏的方法,就像shared_ptr和auto_ptr

  23. 抛出异常的时候,因为要离开函数体,并且局部变量会被析构,所以抛出的对象会被复制构造,并且这个复制的对象异常处理结束后销毁,如果你catch的时候不是catch引用,则会复制两次。这也是现代编译器catch内不是引用类型时会报warning的原因所在

  24. try-catch语句会带来大约5%~10%的代码膨胀,而异常处理生成的代码性能消耗上至少多出了上一条提到的复制,所以效率较低。编译器一般有编译选项可以关闭异常,并且这时候C++标准库的行为会变化,比如原来的抛出bad_alloc异常会变成返回空指针。建议是按80-20的原则,那20%的代码不要使用异常

  25. 使用exception specification的时候要注意函数内所调用的对象是否会抛出预料之外的异常,而导致unexcepted被调用(默认行为是abort掉)

  26. 缓式评估(lazy evaluation)很有用啊很有用,最简单的例子就是写时复制,比如std::string,在赋值操作的时候内部使用引用计数共享同一块内存,等到需要修改对象时,如果引用计数不为1,才复制内容并执行相应修改。所以直接return std::string和赋值是不会照成性能问题滴。需要特别注意的是,有时候在多线程环境下,缓式评估(比如有些写时复制的string操作)并不能带来优化效果。

  27. 帮助编译器完成返回值优化(RVO),具体视不同编译器而言。通常的做法是,只有一个return函数并且在return函数里写构造函数,或者只return一个变量

  28. 要注意隐式类型转换,特别是 a = b + c,a、b、c都能隐式转为int时,执行 a = 10 + c 或 a = b + 10的情况,非常危险

  29. 建议重载操作符时,使用op=来实现op,这样可以减少比如 a = b + c + d + e …操作时编译器优化后产生的临时对象数量

  30. 运行时类型检查(RTTI)比较耗费性能,无论是dynamic_cast还是typeid

  31. 这条是我觉得应该避免的,禁止对象产生在堆之中的方法是把new操作符private了,而强制对象产生在堆里的方法是把构造或析构函数private或protected了,然后通过另外的函数来产生对象,但是这不能解决继承关系下的产生位置限定,所以我觉得非常应该避免

  32. 如上第26所述,缓式评估很NB,但是,在实现的时候要注意写时复制的共享问题,要管理好可共享状态。比如这种行为string a = "Hello world!"; char& c = a[3]; string b = a;这时候改变c的值不应该影响到b

  33. 可以尝试用Proxy Class(代理类)进一步提高缓式评估的效果,比如string的operator[]不返回char&而是一个可以隐式转换成char&的代理类,因为operator[]返回的东西也有可能只需要只读权限

  34. 一个小tips,对于单个类型决定函数执行时可以用virtual继承,而多个类型决定执行的函数的话可以试试类似这种方式。double-dispatches => 两个single-dispatches

    比如两个对象A a和B b分别有虚函数A::func1(Base b) { b->func2(this); }和B::func2(A a)…,则调用a->func1(b)就完成了对两个类型相关函数的自动选择。需要仔细思考,或者《More Effective C++》的第31条有比较完整的例子。也比较好理解

以下是另外的地方看到的附加坑

  1. 宏这个东西很不好玩,比如有些max实现是#define max(a, b) a > b? a: b,如果使用max(x + 3, y)会怎么样?表达式会展开成 x + 3 > y? x + 3: y, 即便是#define max(x, y) (x) > (y)? (x), (y),如果使用max(x, y) + 3 也会展开为 (x) > (y)? (x): (y) + 3,还有类似max(++x, y)这种++操作会执行两次的情况。所以不是条件编译、守护头文件和#pragma禁止警告外,少用宏为妙(不过很多测试和日志套件里用宏来判定行数、文件啊什么的还是很可以有的)。

总结

基本就到这里,比较流水帐,C++其实并不难学,但是有时候如果按常规思维或者其他语言的思维,容易落入陷阱里,导致不明原因的泄漏、崩溃、低效等,所以想要成为一名合格的C++程序员,来踏平C++的各种坑吧。不过理解了C++的各项原理的话,其实其他语言只是自动做了一些工作而已,感觉对学习其他语言的原理上还是很有帮助的。

《C++的坑多吗?》