重载操作符和用户定义的转换

操作符重载里有这样一条规则:赋值操作符(=),下标操作符([]),函数调用操作符(()),指针成员访问箭头(->)必须重载为类成员函数
1、操作符重载
操作符重载使得程序员能够为类类型的操作数定义预定义的操作符版本,重载的操作符使得类类型对象可以与预定义的操作符一起被使用,使得对于类对象的操纵与内置类型的对象一样直观
重载的操作符在类体中被声明,声明方式同普通成员函数一样,只不过它的名字包含关键字operator,以及紧随其后的一个预定义操作符
1.1、类成员与非类成员
编译器只考虑在左操作数的类中定义的成员重载操作符以及在其基类中定义的重载操作符
声明非类成员的重载操作符也是可以的,例如:
bool operator==( const String &, const String & );
bool operator==( const String &, const char * );
全局重载操作符比成员重载操作符多了一个参数,对于成员操作符隐式的this 指针被用作隐式的第一个参数。例如对于成员操作符如下表达式
flower == "lily"
被编译器重写为
flower.operator==( "lily" )
在成员重载操作符的定义中,我们通过this 指针可以引用左操作数flower。对于全局重载操作符,代表左操作数的参数必须被显式指定
当一个重载操作符是一个名字空间的函数时,对于操作符的第一个和第二个参数,即等于操作符的左和右两个操作数都会考虑转换
一般应该怎样决定是把一个操作符声明为类成员还是名字空间成员呢?在某些情况下,程序员没有选择的余地
(1)如果一个重载操作符是类成员,那么只有当跟它一起被使用的左操作数是该类的对象时,它才会被调用。如果该操作符的左操作数必须是其他的类型,那么重载操作符必须是名字空间成员
(2)C++要求赋值= 下标[] 调用() 和成员访问箭头-> 操作符必须被定义为类成员操作符。任何把这些操作符定义为名字空间成员的定义都会被标记为编译时刻错误
1.2、重载操作符的名字
对于内置类型的操作符,它的预定义意义不能被改变,也不能为内置数据类型定义其他的操作符
程序员只能为类类型或枚举类型的操作数定义重载操作符。我们可以这样来实现:把重载操作符声明为类的成员,或者声明为名字空间成员,同时至少有一个类或枚举类型的参数(按值传递或按引用传递)
预定义的操作符优先级不能改变,在使用重载操作符时,可以用小括号来改变优先级
操作符预定义的操作数个数必须被保留
2、友元
    通过把函数或操作符声明为友元,一个类可以授予这个函数或操作符访问其非公有成员的权利
友元声明以关键字friend开始,它只能出现在类定义中。因为友元不是授权类的成员,所以它不受其所在类的声明区域public private 和protected 的影响。
friend bool operator==( const char *, const String & );
友元声明的最常见用法是,允许非成员的重载操作符访问一个视其为朋友的类的私有成员。原因是,除了提供左和右操作数的对称性外,非成员的重载操作符就像成员函数一样,能够完全访问一个类的私有成员
3、操作符=
一个类对象向该类的另一个对象的赋值可通过拷贝赋值操作符来执行
我们也可以为一个类类型定义其他的赋值操作符。如果一个类类型的对象被赋以一个不是它自己类类型的值,那么它可以定义接受这种其他类型参数的赋值操作符。例如:
为了支持用C 风格字符串向String 类对象的赋值,如:
String car ("Volks");
car = "Studebaker";
4、操作符[]
我们可以为表示容器抽象并能够获取其单独元素的类定义下标操作符operator[]()
5、操作符operator()
    如果一个类类型被定义来表示一个操作时,则可以为这个类类型重载函数调用操作符,以便调用这个操作。例如,absInt类被定义为“将取一个int 型操作数的绝对值的操作”封装起来
class absInt {
public:
int operator()( int val ) {
int result = val < 0 ? - val : val;
return result;
}
};
我们通过向一个类类型的对象应用一个实参表,调用该类重载的operator()操作符
6、操作符->
我们也可以为类类型的对象重载成员访问操作符箭头,它必须被定义为一个类的成员函数。它的作用是赋予一个类类型与指针类似的行为。它通常被用于一个代表智能指针的类类型
// 支持指针行为的重载操作符
class ScreenPtr {
public:
Screen& operator*() { return *ptr; }
Screen* operator->() { return ptr; }
//....
};
成员访问操作符箭头被重载为一元操作符,即它没有参数。当它被用在表达式中时,只能根据左边操作数的类型来选择它。例如,下面给出的语句:
point->action();
将检查point 以决定其类型。如果point 是某一个类类型的指针。则这个语句使用内置成员访问操作符箭头的语义。如果point 是某一个类类型的对象或引用,则查找这个类的重载的成员操作符箭头。如果没有定义成员操作符,则该语句就是错的,因为类对象或引用通常必须使用点成员访问操作符来引用类成员。如果定义了重载的成员访问操作符箭头,则它被绑定到point上并被调用
重载的成员访问操作符箭头的返回类型必须是一个类类型的指针,或者是定义该成员访问操作符箭头的类的一个对象。如果返回类型是一个类类型的指针,则内置成员访问操作符箭头的语义被应用在返回值上。如果返回值是另外一个类的对象或引用,则递归应用该过程直到返回的是指针类型或语句错误
7、操作符++和--
重载的递增和递减操作符的前置和后置实例都可以被定义
前置操作符的声明看起来就像你所期望的那样:
class ScreenPtr {
public:
Screen& operator++();
Screen& operator- - ();
// ...
};
前置的递增和递减操作符被定义为一元操作符函数。
为区分后置操作符与前置操作符的声明,重载的递增和递减后置操作符的声明有一个额外的int 类型的参数。在下面的例子中,它声明了ScreenPtr 类的前置和后置操作符对:
class ScreenPtr {
public:
Screen& operator++(); // 前置操作符
Screen& operator- - ();
Screen& operator++(int); // 后置操作符
Screen& operator- - (int);
// ...
};
额外的整型参数对于后置操作符的用户是透明的,编译器为它提供了缺省值
8、操作符new和delete
在缺省情况下,空闲存贮区中的类对象的分配和释放,由在C++标准库中定义的全局操作符new()和delete()来执行。如果一个类提供了两个分别被称为操作符new()和操作符delete()的成员函数,那么它就可以承接自己的内存管理权。如果在类中定义了这些成员操作符,则它们会被调用,以取代全局操作符来分配和释放该类类型的对象
类成员操作符new()的返回类型必须是void*型,并且有一个size_t 类型的参数,这里的size_t 是一个在系统头文件<cstddef>中被定义的typedef。下面是Screen 类操作符new()的声明:
class Screen {
public:
void *operator new( size_t );
// ...
};
当new 表达式创建一个类类型的对象时,编译器查看该类是否有一个成员操作符new()。如果有则选择这个操作符为该类对象分配内存;否则,调用全局操作符new()
类成员操作符delete()的返回类型必须是void,并且第一个参数的类型是void*。下面是Screen 类的操作符delete()的声明:
class Screen {
public:
void operator delete( void* );
};
当delete 表达式的操作数是指向一个类类型对象的指针时,编译器检查该类是否向一个成员操作符delete()。如果有,则选择该操作符为类对象释放内存;否则调用全局操作符delete()
9、用户定义的转换
类的设计者可以为类类型的对象提供一组用户定义的转换,这些用户定义的转换也是由编译器在需要时隐式地调用的
C++提供了一种机制,通过它,每个类都可以定义一组可被应用在该类型对象上的转换
对于SmallInt,我们定义了一个从SmallInt 对象到int 型的转换。下面是实现:
class SmallInt {
public:
SmallInt( int ival ) : value( ival ) { }
// 转换操作符
// SmallInt ==> int
operator int() { return value; }
// 没有提供重载操作符
private:
int value;
};
用户定义的转换是在类类型和转换函数中指定的类型之间的转换。转换函数定义了转换的意义,以及应用转换时编译器必须执行的动作
9.1、转换函数
转换函数是一种特殊类型的类成员函数,它定义了一个由用户定义的转换,以便把一个类对象转换成某种其他的类型。在类体中通过指定关键字operator,并在其后加上转换的目标类型后我们就可以声明转换函数
转换函数采用如下的一般形式:
operator type();
这里的type 可用内置类型、类类型或typedef 名取代。但是不允许type 表示数组或函数类型。转换函数必须是成员函数,它的声明不能指定返回类型和参数表
9.2、用构造函数作为转换函数
在一个类的构造函数中,凡是只带一个参数的构造函数,例如,SmallInt 的构造函数SmallInt(int) 都定义了一组隐式转换,把构造函数的参数类型转换为该类的类型

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值