OOP
- 面向对象:封装是基础,继承是手段,多态是目的
- 泛型编程:参数化类型是基础,模板是手段,通用是目的。
- 面向对象的编程依赖运行时多态,泛型编程是编译时多态。
1. 避免使用vector<bool>
使用deque与bitset来替换vector
deque提供了几乎所有vector所提供的成员函数(缺是reserve和capacity)。并且,deque是一个STL容器,它保存真正的bool值。
bitset不是一个STL容器,但它是C++标准库的一部分
2. 调用empty而不是检查size==0
理论上而言,这二者等价,但是优先使用empty的理由很简单:
对于所有标准容器,empty都是常数时间操作,而某些list的size耗费线性时间。
3.确保容器中的对象副本正确且高效
容器中保存的对象并不是你放进去的对象,取出的对象也不是容器内的对象,它们都是副本。
这种copy in,copy out就是STL 容器的工作方式。
一旦对象放入容器,它们可能会遭遇更多的复制,比如顺序容器内遭遇排序或者插入,删除,它们元素都会进行一次复制操作。复制操作由拷贝构造函数与拷贝构造运算符完成。那么问题来了,如果对象的复制操作很费时,填充对象这个操作可能会降低程序性能,而且放入的副本如果有特殊含义,可能会导致出错。
在存在继承关系的情况下,copy会导致slicing,举例而言,容器元素类型是bc,但我们试图放入dc对象,那么它的特有部分将会丢失。
总之,使copy操作高效且正确的方法是在容器内部包含指针而不是对象。当然,指针也有一些问题,后期我们会发现智能指针更好用。
听起来STL容器的疯狂复制这种行为很蠢,但是相对于array,它的可塑造性强了很多,我们在使用时只需要它总是在复制并且总是应该向其中加入对象的指针即可。
4. 区间成员函数优于与之对应的单元素成员函数
如果我们试图将一个vector1内全部元素替换为vector2的后半部分,最好的办法是这样
v1.assign(v2.begin() + v2.size() / 2, v2.end());
5. 针对性地使用map::operator[]与map::insert
operator[]
默认返回一个value的引用,这在更新操作中再正常不过,但对于insert
操作,它会使用value
的默认构造函数构造一个,然后返回这个新建立对象的引用。具体来说
m[1] = 1.50;//m中不存在k==1
等价于
typedef map<int, Widget> IntWidgetMap;
pair<IntWidgetMap::iterator, bool> result = m.insert(IntWidgetMap::value_type(1, Widget()));
result.first->second = 1.50;
可以看到,通过operator[]
发生了
-
一次建立widget临时对象
一次销毁widget临时对象
一次widget赋值操作
以value初始化widget自然会比上述代码更加高效,所以我们应该直接使用insert代替operator[]
m.insert(IntWidgetMap::value_type(1, 1.50));
6.尽量使用constexpr
constexpr应用于对象时,其类似于const的加强版,但其应用于函数时的意义却与此大相径庭。从概念而言,constexpr不仅仅表示一个常量,也表示该量在编译期即可明确。然而,在constexpr function中,constexpr未必代表着编译期取值,甚至未必能保证const。
6.1 constexpr object
constexpr对象具备const属性,其值在编译期已知 .
编译期即被明确的值具有一定的特权,例如它们可能会被存于只读存储器中,对于嵌入式开发者来说这或许是一个非常重要的特性,但其更广泛适用于C++编译期需要整型常量表达式的环境中,例如数组长度说明、整型模板参数设定、枚举量设定、齐位说明符设定等等。当你需要将一个变量应用于上述环境时,将其声明为constexpr即等效于向编译器保证其可在编译期求值:
需要注意的是const并不具备编译期明确其值的能力:
int sz; // as before
const auto arraySize = sz; // fine, arraySize is const copy of sz
std::array<int, arraySize> data; // error! arraySize's value not known at compilation
onstexpr auto arraySize1 = sz; // error! sz's value not known at compilation
std::array<int, sz> data1; // error! same problem
constexpr auto arraySize2 = 10; // fine, 10 is a compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2 is constexpr
简而言之,所有的constexpr对象都是const的,但并不是所有的const对象都是constexpr。仅有constexpr对象具备编译期明确其值的能力。
6.2 constexpr function
constexpr function的情况与其使用情境相关,如果你以一个compile-time constants调用constexpr function,该函数会产生一个compile-time constants,而如果你以一个runtime values调用它,它则会产生一个runtime value。
- constexpr function的用途
用于必需compile-time constants的环境下(例如数组长度)
如果你传入的参数可在编译期获得,那么constexpr function会在编译期产生结果,反之只要有一个参数不符合compile-time,则代码将无法通过编译。
减少代码重复
当一个constexpr函数被一个或多个runtime values调用时,它会按照普通函数的形式运行。这意味着我们不需要在开发时区分compile-time value与runtime value。