条款22:考虑以操作符复合形式(op=)取代其独身形式(op)
1、确保操作符的复合形式(如operator+=)和其独身形式 (如 operator+ )之间的自然关系能够存在,一个好方法就是以前者为基础实现后者。
例子:
#include<iostream>
using namespace std;
class Rational{
public:
Rational(int n = 0, int d = 1) :numerator(n), denominator(d){}//构造函数刻意不为explicit,为了隐式类型转换
int getNumerator() const{ return numerator; }
int getDenominator() const{ return denominator; }
Rational& operator*=(const Rational& rhs){
this->numerator *= rhs.numerator;
this->denominator *= rhs.denominator;
return *this;
}
private:
int numerator;
int denominator;
};
const Rational operator*(const Rational& lhs, const Rational& rhs){//Rational和Rational相乘
return Rational(lhs) *= rhs;
}
ostream& operator<<(ostream& os, Rational& r){
os << r.getNumerator() << '/' << r.getDenominator();
return os;
}
int main(){
Rational oneEighth(1, 8);
Rational oneFourth(1, 4);
Rational oneHalf(1, 2);
Rational result = oneHalf*oneFourth*oneEighth;
cout << result << endl;
system("pause");
return 0;
}
3个与效率有关的情况需要注意:
第一,一般而言,复合操作符比其对应的独身版本效率高,因为独身版本通常必须返回一个新对象,所以要承担一个临时对象带来的构造和析构成本;复合版本直接把结果写入其左端自变量,所以不需要产生一个临时对象来放置返回值。
第二,如果同时提供某个操作符的复合形式和独身形式,便允许客户在效率与便利性之间做取舍。客户端可以决定是这样编写:
Rational a, b, c, d, result;
...
result = a * b * c * d; // 可能用了3个临时对象,每个operator* 调用使用1个
或是这样编写:
result = a; //不用临时对象
result *= b; // 不用临时对象
result *= c; //不用临时对象
result *= d;//不用临时对象
第三,编译器一般对匿名对象进行返回值优化,而对命名对象通常不能。
const Rational operator*(const Rational& lhs, const Rational& rhs){//Rational和Rational相乘
//实现一
return Rational(lhs) *= rhs;
//实现二
Rational result(lhs);
return result *= rhs;
}
上述例子实现二内含一个命名对象result,返回值优化无法施展于此operator*实现代码身上,实现一总是适用返回值优化。
条款23:考虑使用其他程序库
考虑iostream和stdio程序库,iostream与stdio相比有几个优点:例如它具有类型安全的特性并且可扩充。然而在效率方面,iostream通常表现得比stdio差,因为stdio的可执行文件通常比iostream更小也更快。
不同的程序库即使提供相似的技能,也往往表现出不同的性能取舍策略,所以一旦你找出程序的瓶颈,应该思考是否可能通过改用另一个程序库来消除瓶颈。
条款24:了解virtual functions、multiple inheritance、virtual base classes、runtime typeidentification的成本
1、虚函数会带来以下成本:
a、你必须为每个拥有虚函数的class耗费一个virtual talbe空间,其大小视虚函数的个数(包括继承而来的)而定。
b、你必须在每一个拥有虚函数的对象内付出一个为额外指针的代价,也就是一个virtual table pointer。
c、你事实上放弃了inlining。因为“inline”意味“在编译期,将调用端的调用动作被调用函数的函数本体取代”,而“virtual”意味“直到运行时期才能知道哪个函数被调用”。
2、多重继承会更复杂:
a、找出对象内的vptrs会变得比较复杂。因为一个对象之内会有多个vptrs;除了我们已经讨论过的单独的vtbl以外,针对base classes而形成的特殊vtbls也会被产生出来。因此虚函数对每一个对象和每一个类所造成的空间负担又增加了,运行时期的调用成本也增加了。
b、多继承经常导致对虚基类的需求。虚基类可能导致另一个成本,因为其实现做法常常利用指针,指向虚基类成分,以消除重复现象,这样可能导致对象内的隐藏指针增加。
3、RTTI让我们在运行时期获得对象和类的相关信息,这些信息被存储在类型为type_info的对象里,通过typeid操作符可以获取一个类的type_info对象。C++规范书上说,只有当某种类型拥有至少一个虚函数,才保证我们能够检验该类型对象的动态类型。RTTI的设计理念是根据类的vtbl来实现。
上述内容的具体例子见我的另一篇博客http://blog.csdn.net/ruan875417/article/details/46408475