类成员与非类成员重载操作符
重载操作符使得程序员能够为类类型的操作数定义预定义的操作符版本。重载操作符在类体中被声明,声明方式和普通函数一样,只不过他的名字包含关键字operator,以及紧随其后的一个预定义操作符。
只有左操作数是类类型的对象时,才会考虑使用作为类成员的重载操作符。如果存在string& operator==(const char *)这样的声明的话,调用if("tulip" == flower)时,编译器会报错。但是可能会说,我们可以用类构造函数,从一个C风格的字符串创建一个string类对象,为什么编译器不能隐式地做如下转换呢 if(new string("tulip") == flower)呢?简单的答案就是效率。重载操作符不用要求两个操作数的类型一定相同。
声明为非类的重载操作符也是可以的。对于成员操作符,隐式的this指针被用作隐式的第一个参数,可以通过this指针引用左操作数。而对于全局重载操作符,代表左操作数的参数必须被显式指定。
有了全局重载操作符,如果对于"tulip" == flower的操作,我们没有定义bool operator==(const char *,string &)的话,需要定义吗?答案是可以这样做,但是不是必须得。当重载操作符是一个名字空间的函数时,对于操作符的第一个和第二个参数,即等于操作符的左和右两个操作数都会考虑转换。这意味这编译器将解释为operator==(new string("tulip"),flower),并调用bool operator==(string&,string&)。既然编译器会做转换,那么为什么要提供bool operator==(string &,const char *)呢?想象下如果频繁比较一个c风格字符串和string类型而c风格字符串转化到string类类型开销比较大时,提供这样一个重载操作符会提高效率!
一般该怎样决定将一个操作符声明为类成员还是名字空间的呢?c++要求,赋值=、下标[]、调用() 和成员操作符->必须被定义为类成员操作符。
重载操作符的名字
只有在c++预定义操作符集中的操作符才可以被重载。对于内置类型的操作符,他的预定义无法改变,也不能为其定义其他操作符。程序员只能为类类型或枚举类型定义重载操作符。可以这样实现:把一个重载操作符声明为类的成员、或者声明为一个名字空间成员,同时至少有一个类类型或枚举类型的参数。
操作符=
一个对象想该类另外一个对象的赋值可以通过赋值操作符来进行。如果希望类具有内置类型那种连续赋值操作的话,那么重载赋值操作符返回的类型是该类的一个引用对象。每个赋值操作符必须被定义为类的一个成员函数。
操作符[]
下标操作符必须呗定义为类的成员函数。下标操作符的定义中,要对接受的索引值进行边界检查。
操作符()
可以为类对象重载函数调用操作符。如果一个类类型被定义为表示一个操作时,可以为这个类类型重载函数调用操作符,以便调用这个操作。重载的operator()必须呗声明为成员函数,它的参数表可以有任意数目的参数。
例如定义一个类absInt,定义一个类对象absInt func,那么调用func()是,即调用了absInt::operator()
操作符->
可以为类类型的对象重载成员访问操作符,它被重载为一元操作符,即没有参数。当它被用在表达式中是,只能根据左边操作数的类型来选择他。例如point->action(),将检查point的类型,如果point是一个类类型对象指针的话,则这个语句使用内置的成员访问操作符箭头的语义。如果point是一个类对象或者引用的话,则查找这个类的重载的成员操作符箭头。如果没有定义成员操作符,则该语句就是错的。
重载的成员访问操作符箭头的返回类型必须是一个类类型指针,或者是“定义该成员访问操作符箭头的类”的一个对象。
操作符++和--
由于操作符++和--有前置和后置之分,故而重载的递增和递减操作符的生命有一个额外的int类型的参数,例如
screen& operator++(); //前置操作符
screen& operator--();
screen& operator++(int);//后置操作符
screen& operator--(int);
重载的递增和递减操作符也可以被声明为友元函数。
操作符new和delete
类成员操作符new()返回类型必须是void*,并且有一个size_t类型的参数 void* operator new(size_t);可以使用“全局域解析操作符” ::来选择调用全局操作符new(),例如screen *s=::new screen;操作符delete()返回类型是void,并且第一个参数的类型是void*。
这两个操作符都是类的静态成员,他们遵从静态成员函数的一般限制(静态函数没有this指针,只能访问静态数据成员)。其原因在于,这些操作符在被调用之前,要么在对象被创建之前,要么在对象被销毁之后。