模板声明和定义 编译器编译阶段就要掌握模板的定义,所以模板头文件必须既包含声明也包含定义。
虚函数补充
- 构造函数析构函数内也可以调用虚函数,但是这个调用不具有多态性,和当前构造/析构函数所处理的类型一致,比如基类构造函数被调用时子类对象还不存在,所以只能调用基类的。基类析构函数调用的时候子类对象已经析构完成,所以基类析构函数仍然只能调用基类的虚函数。
- 重写的虚函数不能更改原虚函数的默认实参,这个是C++标准定义的未定义行为
严格弱序三条原则
- 两个关键字不能同时“严格弱序”对方
- a严格弱序b,b严格弱序c,那么a必须严格弱序c
- 如果两个关键字任何一个都不“严格弱序”对方,则两个关键字相等
比如“<”符号,符合上序规则,而“<=”符号不符合。
所有的自定义比较函数都必须遵循严格弱序。
例如sort函数,在sort函数内,弱元素个数大于
_S_threshold
,则使用快速排序,弱快速排序迭代深度大于__depth_limit
则使用堆排序;反之元素个数大于_S_threshold
,则使用插入排序。 其中快速排序的实现:
while (true)
{
while (__comp(*__first, __pivot))
++__first;
--__last;
while (__comp(__pivot, *__last))
--__last;
if (!(__first < __last))
return __first;
std::iter_swap(__first, __last);
++__first;
}
对于对比函数__comp
,如果不遵循严格弱序,且出现first与pivot一直相等的情况,那么__first就不会被return,会一直自增导致越界。
原码这样做的原因,是为了节省比较的运算开销,没有进行边界检查。
尽量少使用智能指针的get()来获取裸指针 get函数直接返回一个对象的裸指针,这个指针是新建的,和原来的智能指针不冲突,所以很容易发生冲突问题。
形参在函数体内的形式一定是左值,哪怕传过来的是右值 如果确定是右值,那么用std::move进行转换 如果不确定,那么用std::forward进行完美转发
引用和指针 经常会被问到指针和引用的适用场景,其实准确讲两者的本质没有差别,对于编译器来说,会利用一个符号表存储引用,而符号表内本身也是存的引用对象的地址,也就是指针,本体没差别。 不过一般情况下说的都是引用比较值引用,比如函数的形参会将参数复制一次,额外损耗性能,如果直接使用引用可以模拟值引用的情况,节省性能。 另一种情况就是引用可以模拟指针的传地址,而不用到处都使用解地址符了。 说一下自己的理解,引用发明出来就是为了方便做“值处理”,也就是实现在函数内也能做到修改原参数变量的功能。原本这个功能利用指针也能解决,但是指针有一个性质就是其指向会被更改,也就是说对指针进行操作的时候可能就不小心更改了其指向了,这是一个很危险的设计,所以在这个层面上尽量使用加了限制还更方便的引用来处理。如果说需要更改指针指向,那么就直接使用指针。 其次,一句话总结就是如果允许参数为空,那就传指针,其他情况下就传引用。
const与constexpr尽量使用
- const&作为函数参数
- 成员参数符合const条件就用const
- 增加泛用性,让编译器进行更高级的优化
vector中的reserve指预留空间,而不真正创建元素对象;resize指直接改变元素大小并创建对象。