以下是一些编程方面的建议,目的是为了提升C++程序的效率(这里的效率以时间为评判标准),欢迎指正和补充
- 场景:当设计方法时,将方法设计成值传递还是引用或者指针传递
建议:当变量所占内存大小与指针所占内存差别不大时(int、double),值传递和引用、指针传递的效率一样,当值传递明显要大于8字节时,采用引用、指针传递会更好(string、自定义变量类型)
原理:当使用引用或者指针时,拷贝的是变量的地址值,大小为8字节,而值传递拷贝的是变量本身 - 场景:当需要递增变量时,使用前置递增还是后置递增
建议:非循环下,由于编译器优化,两者效率一致,无需分别;在循环里面编译器没有进行优化,若只是想要递增一个变量,使用前置递增
原理:递增运算符有两种,前置递增++i,和后置递增i++,前者直接对变量本身递增,返回的是对变量本身的引用,后者则先拷贝原对象,并返回变量的副本,再对原对象进行递增操作。 - 场景:代码1和代码2,哪种方式效率更高?
建议:调查所使用的变量类型的赋值操作函数和(构造函数+析构函数)哪个开销大
原理:对于代码1,需要调用ClassTest的构造函数1次,赋值操作函数(operator=)100次;对于代码2,需要高用(复制)构造函数100次,析构函数100次。 - 场景:当需要强制类型转换类指针时,使用dynamic_cast还是static_cast
建议:注重安全性,使用dynamic_cast,注重效率,使用static_cast(目前平台内使用的都是dynamic_cast)
原理:dynamic_cast会进行类型检查,如果强转的类型不是原指针类型的基类,会返回nullptr,而static_cast直接转换成指定类型,有可能会出错,但不会做检测,效率比较高 - 场景:标准库容器查找指定索引的元素时,使用at(i)还是[i]
建议:注重安全性,使用at(i),注重效率,使用[i]
原理:at(i)会进行边界检测,保证索引值超过数组的容量时依然是安全的,而[i]不会检测,直接访问根据索引值进行偏移得到的地址值,可能会出错,但效率比较高 - 场景:当设计方法时,是否应该将其声明为内联
建议:小粒度的函数声明为内联
原理:调用函数需要保护现场,为局部变量分配内存,函数结束后还要恢复现场等,而内联函数则是把它的代码直接写到调用函数处,所以不需要这些开销,但会使程序的源代码长度变大 - 场景:初始化一个变量时,使用直接初始化(ClassTest ct1(ct2)),还是复制初始化(ClassTest ct1 = ct2)
建议:直接初始化
原理:当用于类类型对象时,初始化的复制形式和直接形式有所不同:直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数。复制初始化首先使用指定构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象 - 场景:需要使用map或者set时,是使用std::map(set),还是std::unorder_map(unorder_set)
建议:注重减少空间开销,使用std::map(set),注重减少时间开销,使用std::unorder_map(unorder_set)
原理:STL中,map对应的数据结构是 红黑树。红黑树是一种近似于平衡的二叉查找树,里面的数据是有序的。在红黑树上做查找操作的时间复杂度为 O(logN)。而 unordered_map 对应 哈希表,哈希表的特点就是查找效率高,时间复杂度为常数级别 O(1), 而额外空间复杂度则要高出许多 - 场景:当需要生成一个变量,并将其加入到标准库容器中时,使用push_back还是emplace_back
建议:使用emplace_back
原理:push_back和emplace_back耗时比较