Chapter 24.重载操作符与转换

概述

重载操作符是C++为类专门设计的一种机制,可以使类对象像内置类型一样的使用

四个不能重载的运算符

.类成员运算符
.* 成员指针运算符
:: 域解析运算符
?: 条件表达式运算符

重载的一些要求

1.重载后的运算符与原先运算符的形参数、操作数数目、优先级、结合性要一致
2.不能重载c++中不存在的运行符
3.重载操作符必须有一个类类型或者枚举类型的操作数
4.+、-、*、&既可作一元操作又可作二元操作符,以操作数数目控制
5.&&、||、, 三个操作符不保证求值顺序且前两个操作符不再具备短路求值的特性

重载为类成员和非类成员

如果定义为类成员,一元操作符无显式形参,二元操作符只有一个形参(另一个是this指向左操作数)

定义为非类成员

算术、关系、位操作、IO流最好定义为普通非类成员
如果为普通非类成员,则声明为friend,因为可能访问类的私有数据成员

eg: 
friend ostream& operator<<(ostream &os,const className &cn);

定义为类成员

赋值=、下标[]、调用()、成员访问->必须定义为类成员(operator= 也可以写成非成员函数,可以在类里面加个私有函数,然后operator=调用,这样可以直接访问私有变量)

一般复合赋值操作符定义为类成员eg:+=、-=

++、--、解引用*通常应定义为类成员

tips: 实际来看 jasoncpp把算术和关系都定义为了成员,这样也简短一些

重载操作符的设计

1.一般不要重载具有内置含义的操作符
eg: 合成的赋值=、取地址&、逗号,、&&、||
2.大多数操作符对类对象没有意义
==(很多算法假定存在eg:find)、!=、!(对象为不为空)、<<、>>(输入和输出)一般可以根据逻辑对应关系和需要进行重载
又比如类中有算术运算和位运算就可以重载
算术操作符和复合赋值操作符
<(关联容器和一些算法假定存在eg:sort)

重载操作符的实例

operator<<和operator>>
>>、<<一般需要定义为非成员,不然就会出现
item<<cout;
eg://operator<<定义为成员
class Test{
public:
	Test(int i):m_i(i){}
	std::ostream& operator<<(std::ostream &os)
	{
		os<<m_i;
		return os;
	}
private:
	int m_i;
};
int main()
{
	Test test(100);
	test<<std::cout;
}

定义为非成员

格式一般如下:

istream& operator>>(istream &is,classType &obj)
{
    is>>..
    return is;
}
ostream& operator<<(ostream &os,const classType &obj)
{
    os<<...//应该尽量少做格式化操作
    return os;
}
eg:
class Test{
public:
	Test(int i):m_i(i){}
	friend std::ostream& operator<<(std::ostream &os,const Test &a);//输出时,Test不会被修改,可为const
	friend std::istream& operator>>(std::istream &is,Test &a);//输入时,Test会被修改,不可为const
private:
	int m_i;
};
std::ostream& operator<<(std::ostream &os,const Test &rhs)
{
	os<<rhs.m_i;
	return os;
}
std::istream& operator>>(std::istream &is,Test &rhs)
{
	int i;
	is>>i;
        if (!is)//简单的流出错处理
	{
		std::cerr<<"std::istream failed!"<<std::endl;
	}
	rhs.m_i=i;
	return is;
}
int main()
{
	Test test(100);
	std::cout<<test<<std::endl;
	std::cin>>test;
	std::cout<<test<<std::endl;
	char ch;
	std::cin>>ch;
	return 0;
}
operator==和operator!=
//声明
eg:
class Test{
public:
	Test(int i):m_i(i){}
	friend inline bool operator==(const Test &a,const Test &b);
	friend inline bool operator!=(const Test &a,const Test &b);
private:
	int m_i;
};
//operator==和operator!=实现
inline bool operator==(const Test &lhs,const Test &rhs)
{
	return (lhs.m_i==rhs.m_i);
}
inline bool operator!=(const Test &  lhs ,const Test &rhs )
{
	return !( lhs .m_i== rhs .m_i);
}
int main()
{
	Test test1(100);
	Test test2(100);
	Test test3(200);
	if (test1==test2)
	{
		std::cout<<"test1 is equal test2!"<<std::endl;
	}
	if (test1!=test3)
	{
		std::cout<<"test1 is not equal test3!"<<std::endl;
	}
}
operator<
//声明
friend bool operator<(const Test &lhs,const Test &rhs);
bool operator<(const Test &rhs);
operator<

//operator<实现
bool operator<(const Test &lhs,const Test &rhs)
{
	if (lhs.m_i<rhs.m_i)
	{
		return true;
	}
	else
	{
		return false;
	}
}
bool Test::operator<(const Test &rhs)
{
	if (m_i<rhs.m_i)
	{
		return true;
	}
	else
	{
		return false;
	}
}

operator+
//声明
	friend Test operator+(const Test &lhs,const Test &rhs);//返回值与+的原意保持一致不用引用
	bool operator<(const Test &rhs);
	//(复合)赋值一般为成员
	Test operator+=(const Test &rhs)
	{
		m_i+=rhs.m_i;
		return *this;
	}
//operator+实现 
//有点类似后自增自减操作符
Test operator+(const Test &lhs,const Test &rhs)
{
    Test ret(lhs);
    ret+=rhs;
    return ret;
}
operator+=

赋值(一般赋值、复合赋值)操作符都返回*this,如这里的+=

例子就在上面,一般可以利用+来简化+=

operator[]  一般用于标准库容器下标重载
1.为了可作为左右值,返回引用
2.需要非const成员返回非const引用和const成员返回const引用两个版本

3.该操作符必须作为成员
eg:
class VecData{
public:
	VecData(const vector<string> &vec):sVec(vec){}
	string& operator[](const size_t index)//返回引用,即可做左右值
	{
		return sVec.at(index);
	}
	const string& operator[](const size_t index) const
	{
		return sVec.at(index);
	}
private:
	vector<string> sVec;
};
int main()
{
	istream_iterator<string> beg(cin),end;
	vector<string> sVec(beg,end);
	copy(sVec.begin(),sVec.end(),ostream_iterator<string>(cout,"\t"));
	VecData data(sVec);
	cout<<data[0]<<endl;
}
operator * 和 operator->  一般用于智能指针类的重载
eg:
class Screen
{
public:
	Screen(int x,int y):m_x(x),m_y(y){}
	void display()
	{
		std::cout<<"x="<<m_x<<"y="<<m_y<<std::endl;
	}
private:
	int m_x;
	int m_y;
};
class SmartPtr{
private:
	friend class ScreenPtr;
	SmartPtr(Screen *p):m_ptr(p),m_use(1){}
	~SmartPtr()
	{
		delete m_ptr;
	}
	Screen *m_ptr;
	size_t m_use;
};
class ScreenPtr{
public:
	ScreenPtr(Screen *p):m_sptr(new SmartPtr(p)){}
	~ScreenPtr()
	{
		if (--m_sptr->m_use==0)
		{
			delete m_sptr;
		}
	}
	ScreenPtr(const ScreenPtr &cp):m_sptr(cp.m_sptr){++m_sptr->m_use;}
	ScreenPtr& operator=(const ScreenPtr &rhs)
	{
		++rhs.m_sptr->m_use;
		if (--m_sptr->m_use==0)
		{
			delete m_sptr;
		}
		m_sptr=rhs.m_sptr;
		return *this;
	}
	Screen& operator *()//解引用,所以返回引用
	{
		return *m_sptr->m_ptr;//返回值,指向Screen *m_ptr
	}
	Screen* operator ->()//->前面相当于一个指针,所以返回指针
	{
		return m_sptr->m_ptr;//返回指针
	}
	//const
	const Screen& operator *() const//解引用,所以返回引用
	{
		return *m_sptr->m_ptr;//返回值
	}
	const Screen* operator ->() const//->前面相当于一个指针,所以返回指针
	{
		return m_sptr->m_ptr;//返回指针
	}
private:
	SmartPtr *m_sptr;
};
int main()
{
	Screen *sc=new Screen(10,20);
	ScreenPtr *sp1=new ScreenPtr(sc);
	(sp1->operator->())->display();
	ScreenPtr sp2(sc);
	sp2->display();//成员访问
        (*sp2).display();//解引用
}
operator++ 和 operator  一般用于迭代器类的设计 
eg:
class IncSub
{
public:
	IncSub(int *b,int *e,int *c):m_beg(b),m_end(e),m_cur(c){}
	IncSub& operator++()//前自增/减返回引用
	{
		if (m_cur == m_end)
		{
			throw out_of_range("m_cur == m_end!");
		}
		++m_cur;//add first
		return *this;//then return *this
	}
	IncSub& operator--()
	{
		if (m_cur<m_beg)
		{
			throw out_of_range("mcur < m_beg!");
		}
		--m_cur;
		return *this;
	}

	IncSub operator++(int)//后自增/减返回原值,用一个int形参来区别于前自增/减
	{
		//类似operator+,先整一个临时变量ret
		IncSub ret(*this);
		++*this;//再++,这里调用了前自增
		return ret;//最后返回 临时变量ret,因为总是存在没有++*this之前的*this,所以条件判断放在前自增/减中
	}
	IncSub operator--(int)
	{
		IncSub ret(*this);
		--*this;
		return ret;
	}

	int getArrayValue() const
	{
		return *m_cur;
	}
private:
	int *m_beg;
	int *m_end;
	int *m_cur;
};
int main()
{
	int iArray[10]={1,2,3,4,5,6,7,8,9,10};
	IncSub is(iArray,iArray+9,iArray+4);//pointer 5
	++is;//pointer 6
	std::cout<<is.getArrayValue()<<endl;//6
	--is;//pointer 5
	std::cout<<is.getArrayValue()<<endl;//5
	is.operator++(0);//显式调用后自增
	std::cout<<is.getArrayValue()<<endl;//6
	IncSub is2(iArray,iArray+9,iArray+9);
	is2++;//在这里就出错了。。因为 ++*this 必定会出错 抛出
	std::cout<<is2.getArrayValue()<<endl;//这句就不会执行了
}
operator()   //调用操作符
eg:
class Gt{
public:
	Gt(string::size_type bound):m_bound(bound){}
	bool operator()(const string &s)
	{
		return m_bound<s.size();
	}
private:
	string::size_type m_bound;
};
int main()
{
	istream_iterator<string> beg(cin),end;
	vector<string> sVec(beg,end);
        for (int i = 0; i < 10; ++i) 
        { 
             cout<<i<<" length words have "<<count_if(sVec.begin(),sVec.end(),Gt(i))<<endl;
        }
}
标准库定义的函数对象

重载了operator()的类的对象,叫做函数对象,因为有类似函数的行为,可以用作算法中的谓词函数
//void sort(_RanIt _First, _RanIt _Last)
// order [_First, _Last), using operator<sort(c.begin(),c.end(),greater<cType>());

functional中自带的函数对象

#include <functional>

plus
minus
multiplies
divides
modulus
negate
equal_to
not_equal_to
greater
less
greater_equal
less_equal
logical_and
logical_or
logical_not
bit_and
bit_or
bit_xor

//从vs2010中拖出来的

negate

logical_not 
只有这两个为一元函数对象,其他都是二元函数对象
eg://简单使用
#include <functional>
#include <iomanip>
int main()
{
	minus<int> ms;
	cout<<ms(10,20)<<endl;//-10
	logical_not<bool> ln;
	cout<<boolalpha<<ln(true)<<endl;//false
}
函数对象的函数适配器
1.绑定器
将一个操作数绑定到给定值,从而将二元函数对象转一元
bind1st(给定值绑定到第一个实参,函数对象)
bind2nd(函数对象,给定值绑定到第二个实参)
eg:
count_if(c.begin(),c.end(),bind2nd(greater_equal<cType>(),10);//>10
2.求反器
not1 一元函数对象真值求反
not2 二元函数对象真值求反
eg:
count_if(c.begin(),c.end(),not1(bind2nd(greater_equal<cType>(),10));//<=10,bind2nd把二元转变了一元,再由not1求一元的反

operator类型() const   //类类型转换操作符

eg:
class SmallInt
{
public:
	SmallInt(int i):m_i(i)
	{
		if (i<0 || i>255)
		{
			throw out_of_range("only 0-255!");
		}
	}
	//转换操作符
	//eg: operator double() const 这样虽然是好了,如果有double的时候
	//如果有一个 long double 类型,用operator int/double() const转换都会出现二义性
	//所以一般只提供一种到内置类型的转换操作符
	operator int() const//转换必须为成员函数,无返回值无形参,同时一般不改变转换后的对象→const
	{
		return m_i;//但是必须返回一个operator后的类型,这里为int
	}
private:
	int m_i;
};

C++回调函数

讲到这简单的说说C++回调函数,这个也挺简单的,这里就偷懒下,用的网上的例子,也是因为无限的搜索才看到C++有个回调函数

说起来很多的类库都有回调函数不是么,比如windows编程、cocos2d-x里的menuCallback等等

回调函数就是一个通过函数指针调用的函数
Callback最本质的特征包括两点:注册和触发,如果回调函数是类成员函数,则必须是静态成员函数或者全局函数来实现回调函数
回调函数可以用于计时器通知事件
回调函数是继续自C语言的,因而,在C++中,应只在与C代码建立接口,或与已有的回调接口打交道时,才使用回调函数,否则用函数对象operator()就行

例子来自http://www.cnblogs.com/c007136/archive/2012/05/28/2521594.html

#include <iostream>
using namespace std;
typedef void (*CALLBACK)( int a, int b );

class CBase
{
public:
    CBase();
public:
    /*
    ** 注册回调函数
    **
    */
    void RegisterCallback( CALLBACK fun, int k, int j );
    /*
    ** 调用回调函数
    **
    */
    void CallCallback();
private:
    /*
    ** 成员变量:
    ** 分别保存回调函数指针
    ** 回调函数中的形参参数
    **
    */
    int m;
    int n;
    CALLBACK func;
};

CBase::CBase()
{
    func = NULL;
}

void CBase::RegisterCallback( CALLBACK fun, int k, int j )
{
    func = fun;
    m = k;
    n = j;
}

void CBase::CallCallback()
{
    func( m, n );
}

// 函数1
void fun1( int a, int b )
{
    cout << "fun1 is called" << endl;
    cout << a << " " << b << endl;
    cout << "*****************" << endl;
}

class CCall
{
public:
    /*
    ** 函数2,类中的函数
    ** 注意这是个静态函数
    */
    static void fun2( int a, int b );
};

void CCall::fun2( int a, int b )
{
    cout << "fun2 is called" << endl;
    cout << a << " " << b << endl;
    cout << "*****************" << endl;
}

void main(void)
{
    CBase bbbbb;
    bbbbb.RegisterCallback( fun1, 1, 2 );
    bbbbb.CallCallback();
    bbbbb.RegisterCallback( CCall::fun2, 10, 20 );
    bbbbb.CallCallback();
}
例子来自http://www.cnblogs.com/c007136/archive/2012/05/28/2522528.html

#include <iostream>
using namespace std;
#include <string>

enum Conversion_Ch
{
    UPPER = 0,   //大写
    LOWER,       //小写
};

/*
** 仿函数
*/
class CFunctor
{
public:
    CFunctor( int conversion );
public:
    /*
    ** 重载小括号
    ** 实现仿函数的核心
    */
    char operator() ( char ch ) const;
private:
    int m_nConversion;
};

CFunctor::CFunctor( int conversion )
    : m_nConversion(conversion)
{
}

char CFunctor::operator() ( char ch ) const
{
    switch ( m_nConversion )
    {
    case UPPER:
        {
            return ch & 0x5F;
        }
        break;
    case LOWER:
        {
            return ch | 0x20;
        }
        break;
    default:
        break;
    }
    return ch;
}

/*
** 转换函数
** 利用仿函数实现
*/
void ConversionFun( const string & str, const CFunctor & functor )
{
    for ( int i = 0; i < (int)str.length(); ++i )
    {
        char ch = str[i];
        cout << functor( ch );
    }
}

typedef char (*pFun)( char ch );

char UpperFun( char ch )
{
    return ch & 0x5F;
}

char LowerFun( char ch )
{
    return ch | 0x20;
}

/*
** 转换函数
** 利用回调函数实现
*/
void ConversionFun( const string & str, const pFun functor )
{
    /* 
    ** 注意实现代码,跟仿函数的一摸一样
    ** 仿函数名称也是来源于此
    */
    for ( int i = 0; i < (int)str.length(); ++i )
    {
        char ch = str[i];
        cout << functor( ch );
    }
}

int main()
{
    string str1 = "AbCdEfGhIjKlMn";
    // 利用仿函数,将str1转换为大写字符串
    ConversionFun( str1, CFunctor(UPPER) );
    cout<< endl;

    // 利用仿函数,将str1转换为小写字符串
    ConversionFun( str1, CFunctor(LOWER) );
    cout<< endl;

    // 利用回调函数,将str1转换为大写字符串
    ConversionFun( str1, UpperFun );
    cout<< endl;

    // 利用回调函数,将str1转换为小写字符串
    ConversionFun( str1, LowerFun );
    cout<< endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值