C++ 操作符重载

操作符介绍

从微机原理中我们就知道了精简指令集计算机RISCReduced Instruction Set Computer中的指令分为操作符和操作数。其中操作按照参数个数分为:一元操作符和二元操作符。发展到C++等高级语言时,操作符又可以根据功能分为:算术、关系、位操作、逻辑运算、成员运算、函数调用、索引(下标)、递增、递减操作符。为了理解操作符重载,简单说一些重要的操作符。

²        一元操作符(Unary Operators

逻辑反

&

取地址

~

异或

*

指针去引用(dereference

+

一元加

++

递增

-

一元负

--

递减

²        二元操作符(Binary Operator

二元操作符的数目远大于一元操作符,如+-*/<>!===!=等,不再详细列出。

²        赋值操作符(Assignment Operators

赋值操作符,即=,是编译器提供的默认操作符,如果操作符一个类没有赋值操作符,将使用编译器的默认赋值操作符。另外,默认操作符不能被继承,即父类虽然重载了赋值操作符,但是如果派生类没有重载此操作符,派生类将还是使用默认赋值操作符,而不是父类的的赋值操作符。最后,赋值操作符不能作为静态成员函数存在,即只能对对象实例使用此操作符。

²        函数调用操作符(Function Call Operators

函数调用操作符,即(),常见于标准模板库STLStandard Template Library的仿函数定义。STL的大多数算法都有带仿函数版本;所谓仿函数即是重载了函数调用操作符的类,此操作符的功能就是调用函数。

²        下标操作符(subscripting)

下标操作符,即[],用于快速引用数组元素。所有的STL容器都提供下标操作符,如vector,如果定义了vector<int> vInts,就能使用vInts[i]的形式来访问其元素。

²        成员访问操作符(Member Access Operators

成员访问操作符有2个:->.,后者不能重载,主要用于访问成员变量。->的重载也比较少见,大多数情况下都不需要重载成员访问操作符。

²        递增和递减操作符(Increment and Decrement Operators

递增操作符,即++;递减操作符,即--。需要注意的时,对于复合类型(非int等简单类型),操作符在前与后有着效率上的区分,如++aa++,后者由于构造临时对象,所以效率更低。

²        不能重载的操作符

对于大多数编译器而言,有一些操作符都是不能重载的。

.

成员访问

.*

Pointer-to-member selection

::

作用域

?:

条件赋值

#

转换位字符串的预处理

##

预处理连接

对不同编译器而言,还有别的操作符不能重载。

操作符重载的通用原则

²        为何进行操作符重载

原因主要有这样一些:

Ø         数学计算的需要,如为复数重载+-*/

Ø         使程序变得精练,如重载<<>>操作符使调试类变得容易;

Ø         标准库函数的需要,如仿函数需要重载()map需要重载<

Ø         对象间操作的需要。

Ø         操作符重载的通用原则

从编程实践中,人们总结了以下一些原则,来指导对操作符的重载:

Ø         不能定义新的操作符,只能重载;

Ø         不能重载针对简单类型的操作符;

Ø         操作符重载,只能在成员函数和友元(全局)两种方法中选择一种;

Ø         操作符的优先级等熟悉不会发生变化;

Ø         重载的操作符不能有默认参数;

Ø         除了赋值操作符和成员访问操作符.,其它操作符都能继承;

Ø         基于成员函数方式的操作符重载的第一个残杀都是调用者类型(或者派生类),不能对第一个参数进行转换。

几类重要操作符重载的说明

²        流操作符<<>>

流操作符<<>>分别对应C++标准库中的istreamostream,一般而言,应该为大多数类重载此操作符;因为输出操作符可用于测试和调试,并方便调用库函数的上层应用者。另外,输出输入操作符函数一种对象持久化的低成本实现方式。

要重载流操作符,通常将此操作符定义为类的友元函数,否则类本身必须使istreamostream的派生类;另外在重载操作符时,还通常返回istream&ostream&,这样返回值可以作为下一个操作符的输入,从而实现操作符的链接,如cout << a1 << a2 << endl,其中a1a2分别为实现了流操作符重载的类A的实例。

流操作符的第一个参数时istreamostream的引用,这是因为需要更新流的内部状态;而第二个参数为类的引用,这对<<而言是为了效率(可以定义为常量引用),对>>而言是需要接受修改的内容。

²        赋值操作符=

赋值操作符的最重要问题就是深Copy的问题,即对指针成员,需要重新申请空间,在copy内容,以免两个实例的成员指向同一块地址;默认赋值操作符是浅Copy的,这时指针成员会指向同一块地址。

其次一个小技巧:需要在重载时返回*this的引用,以实现连续赋值,即a1=a2=1

²        转换操作符()

实现类型间转换通常有2种转换方式(除去强制类型转换,如static_cast):通过构造函数和转换操作符。通过构造函数比较容易,如:

Class MyClass

{

public:

    MyClass(const string&);

};

即实现了把类型string转换为类型MyClass

通过转换操作符,需要在类中重载(),如:

Class MyClass

{

public:

MyClass(const string&);

operator string() const;

};

这样就能在需要的时候调用转换操作符实现转换,如string str = (string)my_class_instance

²        逻辑操作符&&||

这类操作符绝不能够重载,虽然编译器允许此类重载;否则,程序会变得晦涩难懂。如if((p=0) && (strlen(p)>10)的语句中,第一个条件为true,才会检查第2个参数。如果对全局操作符&&重载,以使在任何条件下都要检查第2个参数,如if(operator&&(p!=0, strlen(p)>10)),虽然可以做,但是理解混乱了。

小结

一般而言,其它操作符(除了算术运算符、赋值操作符、下标操作符、流操作符、函数调用操作符等)最好不要去重载,因为这会造成程序可读性的降低。另外,反过来讲,用函数实现该功能却是一个更好的选择。

参考书目

²        Effective C++Scott Meyers.

²        More Effective C++, Scott Meyers.

²        STL源码剖析,侯捷。

你可能感兴趣的:(C++,String,vector,Class,编译器,数学计算)