【more effective c++读书笔记】【第2章】操作符(1)
来源:互联网 发布:esp8266与51单片机 编辑:程序博客网 时间:2024/05/22 03:20
条款5:对定制的“类型转换函数”保持警觉
1、两种函数允许编译器执行隐式转换:单自变量constructor 和隐式类型转换操作符。单自变量constructor是指能够以单一自变量成功调用的constructor,这样的constructor可能声明拥有单一参数,也可能拥有多个参数,并且除了第一参数之外都有默认值。隐式类型转换操作符,是一个拥有奇怪名称的member function:关键词operator之后加上一个类型名称。不能为此函数指定返回值类型,因为其返回值类型基本上已经表现于函数名称上了。
2、最好不要提供任何类型转换函数,因为在你从未打算也未预期的情况下,此类函数可能会被调用,而其结果可能是不正确、不直观的程序行为,很难调试。下面用例子来说明:
一、隐式类型转换操作符例子:
#include<iostream>using namespace std;//参见http://blog.csdn.net/ruan875417/article/details/47260827条款24的例子class Rational{public:Rational(int n = 0, int d = 1) :numerator(n), denominator(d){}//可以把int转换为Rationalint getNumerator() const{ return numerator; }int getDenominator() const{ return denominator; }operator double() const{ //隐式类型转换操作符,将Rational转换为doublereturn static_cast<double>(numerator) / denominator;}/*friend ostream& operator<<(ostream& os, Rational& r){os << r.numerator << '/' << r.denominator;return os;}*/private:int numerator;int denominator;};int main(){Rational oneHalf(1, 2);cout << oneHalf << endl;system("pause");return 0;}
上述例子中不注释掉friend ostream&operator<<(ostream& os, Rational& r)函数的运行结果:
上述例子中注释掉friend ostream&operator<<(ostream& os, Rational& r)函数的运行结果:
上述例子中如果Rational类存在friendostream& operator<<(ostream& os, Rational& r)函数,cout << oneHalf << endl;语句就会调用该函数,但当不存在friend ostream&operator<<(ostream& os, Rational& r)函数时,Rational类存在operator double() const这个隐式类型转换操作符,它就将oneHalf隐式转换为double,cout <<oneHalf << endl;调用也能成功。这说明了隐式类型转换操作符的缺点:可能导致错误(非预期)的函数被调用。
解决方法:以功能对等的另一个函数取代类型转换操作符。
#include<iostream>using namespace std;//参见http://blog.csdn.net/ruan875417/article/details/47260827条款24的例子class Rational{public:Rational(int n = 0, int d = 1) :numerator(n), denominator(d){}//可以把int转换为Rationalint getNumerator() const{ return numerator; }int getDenominator() const{ return denominator; }double asdouble() const{ //将Rational转换为doublereturn static_cast<double>(numerator) / denominator;}private:int numerator;int denominator;};int main(){Rational oneHalf(1, 2);//cout << oneHalf << endl;//错误,Rational没有operator<<cout << oneHalf.asdouble() << endl;//正确system("pause");return 0;}
二、单自变量constructors完成的隐式转换例子:
template<class T>class Array{public:Array(int lowBound, int highBound);Array(int size);T& operator[](int index);...};bool operator == (const Array<int>& lhs, const Array<int>& rhs);Array<int> a(10);Array<int> b(10);....for (int i = 0; i<10; ++i) if (a == b[i])//此处a应该是a[i]才对 do something; else do something else;
上述例子中对于if (a == b[i])语句,希望编译器报错,但却能顺利编译通过,因为b[i]可以通过Array(int size)函数转换为Array<int> object,会产生类似以下代码:
for (int i = 0; i<10; ++i)if (a == static_cast< Array<int> >(b[i]);
此行为还非常没效率,因为每次走过这个循环,必须产生和销毁一个临时的Array<int> objec。
解决方法:
a、用关键字explicit。只要将constructors声明为explicit,编译器便不能因隐式类型转换的需要而调用它们。
explicit Array(int size);//注意,使用explicit
b、C++中有一条规则:没有任何一个转换程序可以内含一个以上的“用户定制转换行为”。即编译器不能两次调用隐式类型转换函数。所以可以这样做:
template<class T>class Array{public:class ArraySize{public:AraaySize(int numElements) : theSize(numElements) {}int size() const { return theSzie; }private:int theSize;};Array(int lowBound, int highBound);Array(ArraySize size);//注意这个新声明...};
上述例子仍然可以用一个int自变量构造起一个Array对象,还能阻止你希望避免的类型转换动作。类似ArraySize这样的classes,往往被称为proxy classes,因为它的每一个对象都是为了其他对象而存在的,好像其他对象的代理人一般。
条款6:区别increment/decrement操作符的前置(prefix)和后置(postfix)形式
1、C++中允许++和--操作符的两种形式(前置式和后置式)拥有重载能力。重载函数是以参数类型来区分的,然而不论是++还是--操作符的前置式或后置式都没有参数,为了区分这两种不同的操作,只好让后置式有一个int自变量,并且在它被调用时,编译器默认给该int指定一个0值。
例子:
#include<iostream>using namespace std;class UPInt{ //unlimited precision intpublic:UPInt(int i) :value(i){}UPInt(UPInt& upint) :value(upint.value){}UPInt& operator++(); //前置式const UPInt operator++(int); //后置式UPInt& operator--(); //前置式const UPInt operator--(int);//后置式friend ostream& operator<<(ostream& os,const UPInt& upint);private:int value;};//前置式:累加后取出UPInt& UPInt::operator++(){//返回一个引用this->value += 1;return *this;}//后置式:取出后累加const UPInt UPInt::operator++(int){//返回一个const对象UPInt oldValue = *this;++(*this);//后置式increment和decrement 操作符的实现应以其前置式兄弟为基础。return oldValue;}//前置式:减后取出UPInt& UPInt::operator--(){//返回一个引用this->value -= 1;return *this;}//后置式:取出后减const UPInt UPInt::operator--(int){//返回一个const对象UPInt oldValue = *this;--(*this);//后置式increment和decrement 操作符的实现应以其前置式兄弟为基础。return oldValue;}ostream& operator<<(ostream& os,const UPInt& upint){os << upint.value;return os;}int main(){UPInt i = 5;cout << i++ << endl;//5cout << ++i << endl;//7cout << i-- << endl;//7cout << --i << endl;//5system("pause");return 0;}
2、后置式操作符并未动用其参数,其参数的唯一目的是为了区别前置式和后置式而已。
3、后置式操作符必须返回一个const对象的原因是如果不是const对象,以下调用将合法:
UPInt i = 5;i++++;
即调用i.operator++(0).operator++(0);
不欢迎上述调用的理由有两个:a、它和内建类型的行为不一致;b、其行为非你所预期,第二个operator++所改变的对象是第一个operator++返回的对象,而不是原对象。i只是被累加一次,违反直觉。
4、相较于前置式操作符,后置式操作符效率会更低,因为后置式需要产生一个临时对象来存储原对象的值,然后再返回原对象的值,会发生构造和析构。所以处理用户定制类型时,应该尽可能使用前置式操作符。
5、确定后置式操作行为和前置式的行为一致的原则是后置式increment和decrement 操作符的实现应以其前置式兄弟为基础。
- 【more effective c++读书笔记】【第2章】操作符(1)
- 【more effective c++读书笔记】【第2章】操作符(2)
- 《More Effective C++》读书笔记-操作符
- 【more effective c++读书笔记】【第1章】基础议题(2)
- <<More Effective C++>>读书笔记2: 运算符
- 《more effective c++》读书笔记
- 【more effective c++读书笔记】【第1章】基础议题(1)
- 【more effective c++读书笔记】【第3章】异常(2)
- 【more effective c++读书笔记】【第4章】效率(2)
- 【more effective c++读书笔记】【第3章】异常(1)
- 【more effective c++读书笔记】【第4章】效率(1)
- <<More Effective C++>>读书笔记5: 技巧(2)
- <<More Effective C++>>读书笔记1: 基础议题
- <<More Effective C++>>读书笔记5: 技巧(1)
- 《More Effective C++》读书笔记一
- 《More Effective C++》读书笔记-异常
- 《More Effective C++》读书笔记-效率
- 《More Effective C++》读书笔记-技术
- 专访民生银行:CPOS平台如何从线下布局移动支付
- Razor:从aspx到cshtml常见错误及正确书写方法
- C/C++ 开源库及示例代码
- MVVM设计模式基础知识--NotificationObject类(Prism框架)
- 社説 20150826 南北協議合意 着実な履行で信頼を醸成せよ
- 【more effective c++读书笔记】【第2章】操作符(1)
- 神经网络中训练数据集、验证数据集和测试数据集的区别
- Java基础 笔记(八)-http
- Java&Android复习
- Delphi dll定义与调用
- OAuth 授权机制
- 用“阳光跟帖”告慰抗战英雄
- 逆元 + 费马定理 + 欧拉定理
- PHP生成随机密码