C++11 之后的代码,不要用 NULL 和 0,使用 nullptr。nullptr的类型为nullptr_t,能够隐式的转换为任何指针,防止歧义。
头文件里不要用 using xxx
,防止名字空间污染。
无论是局部变量还是类的成员变量,初始化都是个好习惯。
尽量不要写不带名字空间的代码。
用 explicit
标记单参数构造函数。
const 变量,const 指针,const 成员函数。
基本类型,函数参数直接传值能更好地利用编译器优化。
C++11 之后尽量避免用 raw pointer 直接获取/释放内存。
std::array 是个不错的选择。
让开发者更清楚虚函数的使用情况。
C++11 后线程使用 std::thread。C++17 文件操作有 std::filesystem。
使用前向声明(如果头文件里只用了指针/引用);避免不必要的模板实例化(大量模板会让编译效率降低);避免无意义的头文件引用。
ModelObject(ModelObject &&) = default;
shared_ptr 的拷贝涉及到引用计数的原子操作和线程安全,会有额外开销。
// Bad Idea std::string somevalue; if (caseA) { somevalue = "Value A"; } else { somevalue = "Value B"; } // Better Idea const std::string somevalue = caseA ? "Value A" : "Value B";
// Bad Idea std::string somevalue; if (caseA) { somevalue = "Value A"; } else if(caseB) { somevalue = "Value B"; } else { somevalue = "Value C"; } // Better Idea const std::string somevalue = [&](){ if (caseA) { return "Value A"; } else if (caseB) { return "Value B"; } else { return "Value C"; } }();
使用智能指针时如果使用 new 来创建对象,会有分配引用计数的额外开销。
构造函数/析构函数的性能可能会比较差(继承的情况,会调用父类的构造函数/析构函数;组合的情况,会调用成员对象的构造函数/析构函数),编码时要能够意识到这种“silent execution”的情况。
unique_ptr 不需要维护引用计数,性能会高一些;对于工厂模式,也建议返回 unique_ptr,有必要时通过类型转换获取 shared_ptr:
std::unique_ptr<ModelObject_Impl> factory(); auto shared = std::shared_ptr<ModelObject_Impl>(factory());
std::endl
相当于 "\n" << std::flush
,多了一次强制刷新缓冲区。
float 精度低,但向量化计算可能比 double 性能高;频繁的 double - float 转换也会导致性能下降,根据场景取舍。
老生常谈的问题,i++ 会多一次拷贝。
// Bad Idea std::cout << someThing() << "\n"; // Good Idea std::cout << someThing() << '\n';
前者是 string,于是需要 range check(tail \0),而后者是单个 char,会省一些开销。
在 C++11 之后,std::bind 实现的功能可以用 lambda 来实现:
// Bad Idea auto f = std::bind(&my_function, "hello", std::placeholders::_1); f("world"); // Good Idea auto f = [](const std::string &s) { return my_function("hello", s); }; f("world");
虚函数由于要维护虚函数表及相对应的指针,比普通函数会多一些开销(构造函数必须初始化vptr(虚函数表);虚函数是通过指针间接调用的,所以必须先得到指向虚函数表的指针,然后再获得正确的函数偏移量);虚函数的额外性能开销比较小,一般可忽略不计,但对于 getter/setter 这种本身就没什么开销的方法要注意是否需要用虚函数。
相比较而言,模板比继承提供了更好的性能,它把对类型的解析提前到编译期间。但使用模板的开发难度增加了。
Return Value Optimization (RVO),属于编译器级别优化,对于返回一个对象的函数,消除临时对象的构造和析构成本。更好的习惯是不返回对象,把要返回的对象作为函数参数(引用或者指针)。或者可以用 Computational Constructor,在构造函数中完成计算。
频繁地分配和回收内存会严重地降低程序的性能。
通过开发专用的内存管理器可以解决这个问题。对专用内存管理器的设计可以从多个角度考虑,常见的两个角度是大小和并发。
大小:
并发:
大部分情况下,灵活性会以速度的较低为代价,随着内存管理的功能和灵活性的增强,执行速度将降低。一般来说,只考虑单线程的固定大小内存管理器性能最高。
最快的代码是直线型的代码:没有条件判断,没有循环,没有调用,没有返回。一般来说,程序的关键路径越像一条直线,执行速度就越快。请记住:短小而带有很多分支的代码要比长而没有分支的代码所用的执行时间长。
使用简单计算代替小分支:分支是性能的敌人。
使用单个锁来保护多个不相关的共享资源,会扩大临界区的范围(临界区指的是一个访问共用资源的程序片段),并造成其它不相关的线程间冲突。
实现可扩展性的技巧是减少或者消除顺序化的代码。下面是一些具体建议:
https://zhuanlan.zhihu.com/p/102419965
https://blog.csdn.net/fengbingchun/article/details/86562213