《c++11学习笔记 (13)》由会员分享,可在线阅读,更多相关《c++11学习笔记 (13)(11页珍藏版)》请在金锄头文库上搜索。
1、除了重载的函数调用运算符 operator()之外,其他重载运算符不能含有默认实参。当以个重载的运算符是成员函数时,this 绑定到左侧运算对象。成员运算符函数的(显式)参数比运算对象的数量少一个。当运算符作用于内置类型的运算对象时,我们无法改变运算的含义。: .* . ?: 这四个运算符不能被重载。我们只能重载已有的运算符,不能发明新的。优先级和结合律不变。data1 + data2;operator+(data1,data2);上述两个是等价调用。因为重载运算符其实是函数调用,逻辑与、逻辑或、和逗号的运算对象求值顺序规则无法保留下来,因此,不应该被重载。还有一个原因是的我们不重载逗号和取址
2、运算符:c+语言已经定义了这两种运算符用于类类型对象时的特殊含义,所以一般不重载,否则它们的行为将异于常态,从而导致类用户无法适应。赋值运算,左侧运算对象和右侧运算对象的值相等,并且运算符应该返回它左侧运算对象的一个引用。成员函数还是非成员函数:赋值(= )、下标( )、调用( ( ) )和成员访问箭头(- )运算符必须是成员函数。复合运算符一般来说应该是成员,但非必须,只一点与赋值运算略有不同。改变对象状态的运算符或者与给定类型密切相关的运算符,如递增、递减和解引用运算符,通常应该是 成员。具有对称性的运算符可能转换任意一端的运算对象,例如算术、想等性、关系和位运算符等,因此他们通常应该是普
3、通非成员函数。如果我们想提供含有类对象的混合型表达式,则运算符必须定义成非成员函数。如 int 和double 相加。当我们把运算符定义成成员函数时,它的左侧运算对象必须是运算符所属类的一个对象。输出运算符的第一个参数是一个非常量 ostream 对象的引用,因为写入内容会改变其状态,是引用因为我们无法直接复制一个 ostream 对象。第二个参数一般是一个常量的引用,该常量使我们想要打印的类类型。引用是因为我们希望避免复制实参。常量是不会改变内容。为了与其他保持一致一般返回 ostream 形参ostream &operator(istream &is , Sales_data &item)
4、double price;is item.bookNo item.units_sold price;if(is)item.revenue = item.units_sold * price;else item = Sales_data( );return is;输入运算符必须处理输入可能失败的情况,而输出运算符不需要。在执行输入运算时可能发生以下错误:当流含有错误类型的数据时读取操作可能失败。当读取操作到达文件末尾或者遇到输入流的其他错误是也会失败。当读取操作发生错误时,输入运算符应该负责从错误中恢复。虽然我们可以使用 failbit,eofbit,badbit 等错误标示,但最好是让标准库来
5、标示错误。算术和关系运算符定义成非成员函数,以允许左右的运算对象进行转换。因为这些运算符一般不需要改变运算对象的状态,所以形参都是常量引用。如果定义了算术运算符,则一般也会定义一个对应的复合运算符。 符合允许符一般来说应该是成员,但非必须,只一点与赋值运算略有不同。如果类同时定义了算术运算符合相关的复合赋值运算符,则通常情况下应该使用复合赋值运算符来实现算术运算符。Sales_data operator+(const Sales_data &lhs , const Sales_data &rhs)Sales_data sum = lhs;sum += rhs;return sum;bool o
6、perator = (const Sales_data &lhs, const Sales_data &rhs)return lhs.isbn()=rhs.isbn() & lhs.units_sold=rhs.units_sold & lhs.revenue = rhs.revenue;bool operator != (cosnt Sales_data &lhs, const Sales_data &rhs)return !(lhs = rhs);如果某个类在逻辑上有相等的含义,则应该定义 operator = 。特别因为关联容器和一些算法要用到小于运算符,所欲定义 operator v;
7、v = a , an , the ;为 StrVec 添加这个运算符。class StrVecpublic:StrVec ;StrVec& StrVec:operator = (std:initializer_list)auto data = alloc_n_copy(il.begin() , il.end() );free( );elements = data.first;first_free = cap =data.second;return *this;赋值运算符必须是成员函数。复合赋值运算符不非得是类的成员, 不过我们还是倾向于把包括复合赋值在内的所有赋值运算都定义在类的内部。与标准库
8、一致,返回其左侧运算对象的引用Sales_data& Sales_data:operator +=(const Sales_data &rhs)units_sold += rhs.units_sold;revenue += rhs.revenue;return *this;下标运算符必须是成员函数。为了与原始定义兼容。下标返回的是访问元素的引用。这样做的好处是下标可以出现在赋值运算符的任意一端。进一步,我们最好定义下标运算的常量版本和非常量版本,当作用一个常量对象时,下标运算符返回常量引用以确保我们不会给返回的对象赋值。class StrVecpublic:std:string& operat
9、or (std:size_t n)return elementsn;const std:string& operator (std:size_t n) const return elementsn;private:std:string *elements;在迭代器类中通常有递增和递减运算符,不必须是成员函数,但因为它们改变的正好是所操作对象的状态,所以建议使用成员函数。为了与内置版本一致,前置返回的是递增后的对象的引用。后值饭会的是对象的原值,返回的形式是一个值而非引用。/前置版本class StrBlobPtrpublic: StrBlobPtrStrBlobPtr;StrBlobPtr&
10、StrBlobPtr:operator+()/如果 curr 已经指向容器尾后位置,则无法递增它check(curr,increment past end of StrBlobPtr);+curr;return *this;StrBlobPtr& StrBlobPtr:operator-()/如果 curr 是 0,则继续递减它将产生一个无效下标-curr; /如果 curr 已经是 0,那么我们传递个 check 的值将是一个表示无效下标的非常大的正整数check(curr , decrement past begin of StrBlobPtr);return *this;为了区分前置和后
11、置版本,后置版本接受一个额外的 int 类型的形参。只是为了区分前置和后置的函数class StrBlobPtrpublic:StrBlobPtr operator+(int);StrBlobPtr operator-(int);StrBlobPtr StrBlobPtr:operator+()/ 此处无效检查有效性,调用前置递增运算时才需要检查。StrBlobPtr ret = *this;+*this;return ret;StrBlobPtr StrBlobPtr:operator-(int)StrBlobPtr ret = *this;-*this;return ret;我们的后置版本
12、是通过前置版本完成工作的。因为我们不会用到 int 形参,所以无需为其命名。显示调用:StrBlobPtr p(al);p.operator+(0); /后置版本p.operator+();/前置版本在迭代器类和智能指针类中常常用到解引用运算符(*)和箭头运算符(-)class StrBlobPtrpublic:std:string& operator* ( )const auto p = check(curr,dereference past end);return (*p)curr;std:string* operator-( ) constreturn ;箭头运算符必须是类的成员。解引用
13、运算符通常也是类的成员函数,尽管非必须。返回值分别是非常量 string 的引用或指针,因为一个 StrBlobPtr 智能绑定非常量的StrBlob 对象(构造函数接受非 const)和大多数其他运算符一样,我们可以令 operator*完成我们指定的操作。但是箭头运算符永远不能丢弃成员范文这个最基本的含义。我们可以改变箭头从哪个对象当中获取成员,而箭头回去成员这一事实永远不变。重载的箭头运算符必须返回类的指针或者自定义了箭头运算符的某个类的对象。如果类重载了函数调用运算符,则我们可以像使用函数一样使用该类。因为这样的类同时可以存储状态,所以比函数更灵活。函数调用运算符必须是成原函数。一个类
14、可以定义多个不同版本的调用运算符,相互之间应该在参数数量或类型上有所区别。如果类定义了调用运算符,则该类的对象称作函数对象。class PrintStringpublic:PrintString(ostream &o = cout, char c = ) : os(o) , sep(c) void operator( ) (const string private:ostream char sep;函数对象常常用作泛型算法的实参。for_each(vs.begin() , vs.end() , PrintString(cerr, n);for_each 的第三个参数是类型 PrintStrin
15、g 的一个临时对象,其中我们用 cerr 和换行符初始化该对象。当程序调用 for_each 时,将会把 vs 中的没个元素打印到 cerr 中,元素之间以换行符分隔。当我们编写了一个 lambda 表达式后,编译器将该表达式翻译成以个未命名的对象。lambda 表达式中含有一个重载的函数调运运算符。默认情况下,lambda 表达式不能改变他捕获的变量,因此默认情况,由 lambda 产生的类中的函数调用运算符是以个 const 成员函数。如果被声明成可变的,则调用运算符就不是 const 了捕获的变量被拷贝到 lambda 表达式中,因此,这种 lambda 表达式产生的类必须为每个值捕获变量建立对应的数据成员,同时建立构造函数。令其使用捕获的变量的值类初始化数据成员。如:auto wc = find_if(words.begin(),words.end(),sz(const string &a) return a.size( ) =sz;);该 lambda 表达式产生的类是:class SizeCompSizeComp(size_t n):sz(n) bool operator( ) (const string &s) const return s.size() = sz; private:size_t sz;auto w