首页 > 代码库 > C++primer读书笔记8重载操作符

C++primer读书笔记8重载操作符

函数的重载操作符中也有不少细节


1 重载操作符特点
重载操作符本质上就是重载函数,它的特点:
<1>除了函数调用操作符‘()’外,所有的重载操作符的形参数目必须与操作符的操作数目相等。
<2>重载操作符的操作数必须至少有一个类类型,否则就不是重载而是原型了。
<3>优先级和结合性不会发生改变,例如对于算数操作符的重载,*的优先级肯定还是大于+
<4>不具有短路求值特性(就是根据已有的值就可以判断出最终的逻辑结果的话就会停止计算)
<5>操作符可以可以是类的成员函数,或者普通的非成员函数。
区别是当是类的成员函数,那么this形参为第一个操作数
<6>重载操作符的使用,既可以像操作符之前的调用方式那样进行操作,也可以像函数调用那样。
Item item1 ,item2;
item1 + item2;		// call method 1
operator+(item1,item2)	// call method 2
// if + is operator of class
item1 + item2;
item1.operator+(item2);


2 重载操作符的谨记

<1> 赋值操作符,取地址操作符,逗号操作符
这些操作符对类类型都有默认的含义,也就是如果没有自己重载,编译器就会自己去合成这些这些操作符函数。
这些操作符都是具有自己的意义的,如果我们自己重写就会失去原本的意义。
<2> 赋值操作‘=’,下标‘[]‘,箭头‘->’,调用操作度‘()’必须定义为类成员,定义为类成员操作符,否则会出现变异错误。
<3>复合赋值操作符‘+=’等,一斤改变类状态的操作符自增,自减等一般也要定义为类成员
<4>对称的操作符如,算数运算符 ‘+’,‘-’,‘*’,‘%’一般最好定义为非成员操作符

3 类成员操作符
类成员操作符比非成员操作符要看起来要少一个参数,其实这个少的参数是this参数,而且他被限定为操作符的第一个操作数。


4 输入输出操作符的重载
<1>输出操作符中应该尽量包含少的格式化,因为这样可能会违背使用者要表达的意愿
<2>IO操作符必须为非成员函数
这个说一定是为了兼容对象的输出以及一般类型变量的输出。但是如果把输出操作符设置为类成员函数并没有编


译错误,并且实际上也是可行的。如下所示:
// output.h
#pragma once
#include <iostream>
using namespace std;
class Input
{
public:
Input(void);
~Input(void);
ostream& operator <<(ostream& out);
private:
string str;
int fun;
};


// output.cpp
#include "Input.h"
Input::Input(void)
{
str = "keep move on";
fun = 1001;
}
Input::~Input(void)
{
}
ostream& Input::operator<<(ostream& out)
{
out<<"persist on"<<fun<<endl;
return out;
}


//main.cpp
#include "Input.h"
void main()
{
Input in;
in<<cout<<"I'll always find you"<<endl;
}

输出结果:
persist on1001
I‘ll always find you

结论:这样因为对象必须作为第一个参数,如:in<<cout<<"I‘ll always find you"<<endl;这样和平时的书写习惯不一致,所以最好不好把输出输入操作符定义为类成员操作符。


5 算数操作符的重载
算数操作符要谨记的是它返回一个新的值,不是引用,这个值是两个值计算的结果。因为如果返回的是临时变量的引用的话,临时变量会被销毁。

6 相等操作符
通常比较每个类的数据成员,如果所有的对应成员都相等,那么就认为两个对象相等。

7 关系操作符
关系操作符通常用于需要对类对象进行排序的时候,所以它和相等操作符有点不一样,例如小于操作符只需要类对象数据成员中的某一个而不是所有的,容器中的对象的相等的定义也是根据‘<’来定义的,如果 A !<B && B 
!<A ,那么A和B就相等。

8 赋值操作符
<1>一个类可以定义多个赋值操作符
<2>没有自己定义赋值操作符,编译器会自己定义一个
<3>赋值操作符必须返回对*this的引用,这样就不需要重新创建副本,销毁之前的版本一系列复杂的操作


9 下标操作符
C++中的容器在检索单个元素的时候一般会定义下标操作符‘[]’,因为下标操作符是跟类对象密切相关的,所以它必须定义为类的成员。定义类的下标操作符的时候,一般要定义连个版本,一个是const成员并返回const引用,一个是非const成员并且
返回引用
#pragma once
#include <vector>
using namespace std;
class XB
{
public:
XB(void);
~XB(void);
int& operator [](const size_t index);
const int& operator [](const size_t index)const;
private:
vector<int> data;
};


//XB.cpp
#include "XB.h"
XB::XB(void)
{
data = http://www.mamicode.com/vector(5,2);>
10 解引用操作符‘*‘和箭头操作符‘->‘
这两个操作符经常用在智能指针上面,就是把一些类对象给它赋予指针的功能。智能指针除了要操作的类外,另外包含两个类,一个类用来存放类对象的指针,以及使用计数。另一个类用来操作使用计数,并根据使用计数的大小来确定是否释放对象。下面的例子中Tv类是要操作类,TvPtr是包含使用技术和操作类的类。
#include <iostream>
using namespace std;
class Tv
{
public:
Tv(void):m_height(100),m_width(390){};
~Tv(void){};
int func(){return m_height * m_width;}
private:
int m_height;
int m_width;
};


class TvPtr
{
friend class TvPtrCnt;
Tv* sp;
size_t use;
TvPtr(Tv* p):sp(p),use(1){}
~TvPtr(){delete sp;}
};


class TvPtrCnt
{
public:
TvPtrCnt(Tv* p):ptr(new TvPtr(p)){}
TvPtrCnt(const TvPtrCnt& orig):ptr(orig.ptr){++ptr->use;};
TvPtrCnt& operator =(const TvPtrCnt &orig){++ptr->use;}
~TvPtrCnt(){if(--ptr->use ==0) delete ptr;}


Tv& operator*(){return *ptr->sp;}
Tv* operator->(){return ptr->sp;}
const Tv& operator*()const{return *ptr->sp;}
const Tv* operator->()const{return ptr->sp;}
private:
TvPtr* ptr;
};
void main()
{
Tv *p = new Tv();
TvPtrCnt zp(p);
std::cout<<(*zp).func()<<std::endl;
std::cout<<zp->func()<<std::endl;

system("pause");
}

注意:这里说的是箭头操作符‘->’它不同于一般的操作符,就那point->action来讲:
<1>如果point是指针,指向具有action成员的对象,那么编译器将代码编译为调用该对象的action的成员
<2>如果action是定义了‘->’操作符的类的一个成员,那么point->action与point.operation->()->action等同
<3>否则,代码错误

总结:使用箭头‘->’操作符,必须返回一个指针类型或者自定义了‘->’的类对象。
解引用操作符和箭头操作符赋予了类对象看似指针的行为功能。


11 自增操作符,自减操作符
这些操作符又分为前自增(prefix),后自增(postfix),前自减(prefix),后自减(postfix)
<1>prefix
XXX& operator++();
XXX& operator--();
<2>postfix
为了区分前和后的区别,在后自增,后自减的函数中增加了一个int的参数,编译器提供0做位实参值
XXX operator++(int);
XXX operator--(int);
注意:后自增,后自减的返回值不是引用,因为返回的是一个临时的变量
<3> 显式的调用前缀后缀操作符
XXX.operator++(); //prefix
XXX.operator++(0); //postfix


12 调用操作符和函数对象

<1>
类重载函数调用操作符‘()‘,这样通过控制通过在对象后面加括弧,加参数,可以把类对象当做函数一样使用,我们把定义了调用操作符的类,器对象成为函数对象。
#include <iostream>
using namespace std;
class Abs
{
public:
Abs(){}
~Abs(){}
int operator ()(int val){return val <0 ? -val : val;}
};
void main()
{
Abs XX;
cout<<XX(-108)<<endl;
system("pause");
}

<2>函数对象用于标准库算法
标准库中有很多函数,需要提供函数指针或者函数对象作为参数,例如count_if()函数
<3>标准卡中还有很多定义的函数对象
plus<Type>,minus<Type>,multiplies<Type>,greater<Type>...
很多函数对象都是存在<functional>头文件中的函数对象
plus<int> Add;
cout<<Add(100,-23)<<endl;

C++primer读书笔记8重载操作符