带你读《2022技术人的百宝黑皮书》——19条跨端cpp开发有效经验总结(3)https://developer.aliyun.com/article/1340927?groupCode=taobaotech
Assert的使用
Assert在pc时代是作为一个广泛(甚至是烂泛)使用的警告处理方式,在移动端以及类unix系统中,debug下表现通常会比windows更加猛烈些,通常是阻塞式的处理,特别是移动端会导致程序继续运行不下去,不像windows弹 个框给你一个continue的选项。
因此在跨端开发中应避免直接使用assert,可以考虑使用重定义后的assert,同时合情合理使用重定义后的assert。
#ifdef NDEBUG #define ALOG_ASSERT(_Expression) ((void)0) #else #define ALOG_ASSERT(_Expression) do {
5 |
... |
\ 这里可以额外做error级别日志输出,是否进行assert阻塞式处理。 |
6 |
if(HandleAssert()) |
\ |
7 |
{ |
\ |
8 |
assert(_Expression); |
\ |
9 |
} |
\ |
10 |
} while (false) |
|
11 |
#endif |
|
关于继承
Composition is often more appropriate than inheritance. When using inheritance, make it public.
google的这个定义应该还是非常准确的,通常组合比继承更合适,即时要使用也必须是publice的方式。应尽量保持“is a”的情况下使用继承,如果你想使用私有继承, 你应该替换成把基类的实例作为成员对象的方式。
对于重载的虚函数或虚析构函数, 使用 override, 或 (较不常用的) final 关键字显式地进行标记. 在部分clang编译器下,编译器要求务必显示声明,否则会报错,ms则没有此类要求。
关于Static变量
感兴趣的小伙伴可以研究一下c++的特性““Dynamic Initialization and Destruction with Concurrency”,其中里面有定义静态、动态变量析构的顺序,线程生命周期的对象全部在静态变量之前析构,静态变量按照后构造的先析构的栈式顺序释放。实际在实践中发现apple的clang编译器和运行时库对c++11的这个特性支持,未实现静态变量析构的多线程安全。
因此在目前阶段,如果有用到全局静态变量时需要考虑到析构多线程安全的问题,否则线上在个别平台会发生crash。
一个比较简单的思路:从全局静态变量替换为局部静态变量且不释放,直到进程被kill。这里还有一个变相的好处: 把加载时机从load变成了此代码段真正运行时。
eg: old: static std::recursive_mutex& m_mutex; new: static std::recursive_mutex& mutex() { static std::recursive_mutex& mutex = *(new std::recursive_mutex()); return mutex; }
关于模板
模板的出现极大的方便了程序员,在未进入跨终端领域之前,虽了解它的一些诟病(代码膨胀&不合理的使用带来的性能损耗),也一直认为是一个非常棒的feature,随着移动端对包大小的要求越来越严格,模板的使用在跨终端 上被限制,需要更为合理的使用,否则将膨胀的非常厉害。在漫长的去模板化过程中有些经验值可以输出,供大家参考。
- 在涉及到移动端的跨终端开发里,应尽量避免使用模板,除非它带来足够多的收益,比如json序列化,通篇用cjson的方式替换,从开发体验和代码膨胀比上来看,替换就显得不值得,比如自定义std标准容器,看似省了不少膨胀,但是代码的维护性和可读性降低了很多,同样不值得替换。
- 尽可能选择小的模板编译单元,比如原来一个模板类,改为类里的模板函数
- 通常情况下模板可以以各种方式被除去,这里不是说在裸写一遍模板换实参的方法。
- 应尽可能的减少模板膨胀的速度,换句话说如果有可能应该尽量限制模板被特化的可能,譬如,我们的日志序列化,对于任意struct或者class在实现了ToString()方法后均可以实现日志自动化输出,任意类型在进入到LOG_IMPL中都会生成一份具体类型的实体,经过略微改造后,限制需要被序列化的类型需要显示继承IOBJECT的接口类,改造后,在同样进入到LOG_IMPL中所有的类型只会有一份类型(IOBJECT*)实例化,此举在实践过程中大约减少了我们五分之一的包大小。
- 在多重继承中,特别是公共模块基类如果包含模板,去模板的收益一般会比较大,因尽量限制基类中出现模板, 除非必要,否则应以任何方式替换。
最后再插一嘴,模板对于使用者确实是极大的方便,但是在跨终端领域似乎对于模板的构建者有着更为严格的要求,需要着重考虑如何避免被膨胀,此外对于性能的要求也更为严格,c++11里有不少提供模板性能的方式,&&配 合std::forward实现完美转发,等等,有兴趣的可以看下《Effective Modern C++》。
以上也适用于 宏。
带你读《2022技术人的百宝黑皮书》——19条跨端cpp开发有效经验总结(5)https://developer.aliyun.com/article/1340925?groupCode=taobaotech