带你读《2022技术人的百宝黑皮书》——19条跨端cpp开发有效经验总结(4)

简介: 带你读《2022技术人的百宝黑皮书》——19条跨端cpp开发有效经验总结(4)

带你读《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,随着移动端对包大小的要求越来越严格,模板的使用在跨终端    上被限制,需要更为合理的使用,否则将膨胀的非常厉害。在漫长的去模板化过程中有些经验值可以输出,供大家参考。

  1. 在涉及到移动端的跨终端开发里,应尽量避免使用模板,除非它带来足够多的收益,比如json序列化,通篇用cjson的方式替换,从开发体验和代码膨胀比上来看,替换就显得不值得,比如自定义std标准容器,看似省了不少膨胀,但是代码的维护性和可读性降低了很多,同样不值得替换。
  2. 尽可能选择小的模板编译单元,比如原来一个模板类,改为类里的模板函数
  3. 通常情况下模板可以以各种方式被除去,这里不是说在裸写一遍模板换实参的方法。
  4. 应尽可能的减少模板膨胀的速度,换句话说如果有可能应该尽量限制模板被特化的可能,譬如,我们的日志序列化,对于任意struct或者class在实现了ToString()方法后均可以实现日志自动化输出,任意类型在进入到LOG_IMPL中都会生成一份具体类型的实体,经过略微改造后,限制需要被序列化的类型需要显示继承IOBJECT的接口类,改造后,在同样进入到LOG_IMPL中所有的类型只会有一份类型(IOBJECT*)实化,此举在实践过程中大约减少了我们五分之一的包大小。
  5. 在多重继承中,特别是公共模块基类如果包含模板,去模板的收益一般会比较大,因尽量限制基类中出现模板, 除非必要,否则应以任何方式替换。

 

最后再插一嘴,模板对于使用者确实是极大的方便,但是在跨终端领域似乎对于模板的构建者有着更为严格的要求,需要着重考虑如何避免被膨胀,此外对于性能的要求也更为严格,c++11里有不少提供模板性能的方式,&&配 合std::forward实现完美转发,等等,有兴趣的可以看下《Effective Modern C++》。

 

以上也适用于 宏。

 

带你读《2022技术人的百宝黑皮书》——19条跨端cpp开发有效经验总结(5)https://developer.aliyun.com/article/1340925?groupCode=taobaotech

相关文章
|
Linux
一个进程最多可以创建多少个线程基本分析
一个进程最多可以创建多少个线程基本分析
928 1
|
11月前
|
设计模式 Prometheus 监控
并发设计模式实战系列(20):扇出/扇入模式(Fan-Out/Fan-In)(完结篇)
🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第二十章,废话不多说直接开始~
347 0
|
网络协议 算法 Linux
深度解密 TCP 三次握手与四次挥手
深度解密 TCP 三次握手与四次挥手
653 9
|
安全 调度 C++
互斥锁 vs 自旋锁:底层机制详细解析
互斥锁 vs 自旋锁:底层机制详细解析
586 1
|
存储 NoSQL Linux
定时器的实现方案:红黑树和多级时间轮
定时器的实现方案:红黑树和多级时间轮
|
存储 机器学习/深度学习 缓存
万字详解C++内存池:提高内存分配效率的利器(上)
万字详解C++内存池:提高内存分配效率的利器
万字详解C++内存池:提高内存分配效率的利器(上)
|
编译器 C++
【C++11保姆级教程】delete和default关键字
【C++11保姆级教程】delete和default关键字
887 0
|
安全 算法 编译器
【C++ 泛型编程 进阶篇】C++ 元模板推导函数调用的结果类型 std::result_of/std::invoke_result全面教程
【C++ 泛型编程 进阶篇】C++ 元模板推导函数调用的结果类型 std::result_of/std::invoke_result全面教程
1300 0
|
应用服务中间件 nginx
Nginx源码阅读:nginx_shmtx共享互斥锁(进程锁)
Nginx源码阅读:nginx_shmtx共享互斥锁(进程锁)
338 0
|
缓存 网络协议 算法
窗口到底有多滑动?揭秘TCP/IP滑动窗口的工作原理
当涉及网络性能优化和数据传输可靠性时,TCP/IP滑动窗口是一个关键的技术。本文的摘要将深入揭示TCP/IP滑动窗口的工作原理,探讨其在确保数据准确性和实现高效通信方面的重要性。通过对滑动窗口大小、流控制和数据包确认机制的解析,我们将揭示如何通过优化窗口大小和流控制参数来提升网络性能。此外,我们还将介绍滑动窗口在解决网络拥塞和丢包问题方面的作用,以及如何通过精准的窗口调整实现零丢失、百分之百到达的数据传输。通过理解滑动窗口的工作原理,读者将能够更好地理解网络通信的内部机制,并为优化其应用程序的性能提供有价值的见解。
805 0
窗口到底有多滑动?揭秘TCP/IP滑动窗口的工作原理

热门文章

最新文章