条款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版权声明:本文为博主原创文章,未经博主允许不得转载。