您的位置:首页 > 编程语言 > C语言/C++

C++中的操作符重载

2010-04-01 15:53 190 查看
1.概述

C++中对类对象(抽象数据类型实例)的操作通过向对象发送消息完成,使用C++的操作符链接用户自定义类型对象的过程叫做操作符重载,编译器在处理表达式时,会根据操作符的使用方式生成合适的代码。

C++强大的功能不仅在于用户可以自己定义新的类型,还表现在允许重载现有的大多数操作符,使这些操作符可以与类对象结合使用,给操作符赋予新类型的含义。

操作符重载通过编写函数定义实现,被重载的函数名格式为operator+运算符,作用于类对象的操作符必须进行重载,有以下两个例外:

1.赋值操作符无需显式重载就可以作用域每一种类型,默认行为是为类的数据成员赋值,这种默认的行为对含有指针类型的数据成员的类是危险的,对于含有指针数据成员的类,赋值操作符必须进行重载;

2.地址操作符无需重载就可以英语与各种类对象,返回对象的内存地址,地址操作符也可以进行重载。

可以重载C++内部定义的操作符,这样可以为用户自定义的类型提供简洁的表达式,必须手动编写操作符重载函数,这些函数可以作为成员函数,也可以作为友元函数。

一些限制:

a.不改变优先级

b.不改变结合性

c.不改变操作数的个数

d.不改变作用域内部类型的方式

操作符重载只能随用户自定义类型的对象一起使用,或者随用户自定义类型的对象和内部类型对象混合使用,这样可以防止改变作用域内部类型的方式。

操作符只能够显式重载,要保证相关操作符的一致性,可以用一个操作符实现另一个操作符。

2.类成员函数和友元函数

在重载操作符()[]->或者任何赋值操作符时,操作符重载函数必须声明为类的一个成员;

如果操作符的左操作数是类的对象或者类对象的引用 ,才可以用成员函数实现,如果左操作数不是类的对象,也不是类对象的引用,则只能用一个非成员函数实现,如流插入操作符和流读取操作符。

只有当二元操作符左操作数是该类的一个对象,或者一元操作符的操作数是该类的一个对象时,才有必要定义为类的操作符成员函数。

选择非成员函数重载操作符的另一个原因是使操作符具有可交换性。

3.

(1)重载流插入和流读取运算符

a.这两个运算符本身可以处理char*字符串和指针在内的各种内部数据类型;

b.这两个操作符重载函数只能够定义为类的友元函数

friend ostream & operator <<(ostream & output,const PhoneNumber &);
friend istream & operator >>(istream & input,PhoneNumber &);


操作符重载函数包含两个参数,一个是对istream/ostream的引用,另一个是对用户自定义类型的引用,当编译器遇到语句

cin>>phone时,编译器将生成函数调用operator>>(cin,phone)

同样,编译器督导cout<<phone时,编译器将生成函数调用operator<<(cout,phone)

(2)重载一元操作符

可重载为无参数的非静态成员函数和有一个参数的非成员函数,参数必须是用户自定义的类对象或者对象的引用,实现操作符重载的成员函数应该定义为非静态类型,以便访问类的非静态数据,静态函数只能够访问类的静态数据。

重载一元操作符时,要将重载函数定义为成员函数而非友元函数,这样可以避免破坏类的封装性。

(3)重载二元操作符

可以重载为带一个参数的非静态成员函数,也可以重载为带两个参数的友元函数,其中一个参数是类对象或者类对象的引用,一般情况下,如果操作符的左操作数是类对象或者引用,则定义为成员函数比较好。

(4)拷贝构造函数

下标运算符的重载函数需要返回值的引用,这样可以作为左值,进行赋值。

需要复制对象时,需要调用复制构造函数,通过建立对象的副本(拷贝),初始化相同类型的对象(构造函数),必须谨慎对待拷贝构造函数,避免两个对象指向同一个动态分配的存储区,需要复制对象时,都需要创建拷贝构造函数

复制构造函数应使用引用调用,而非传值调用,否则复制构造函数会出现无穷递归,因为对于传值调用,建立传入复制构造函数的对象副本会造成复制构造函数的递归调用。

如果构造函数仅把源对象的指针复制到目标对象的指针,这两个对象将指向同一块动态分配的内存块,执行析构函数释放该内存块,结果将导致另一个对象的ptr没有定义。

类的对象包含的指向动态分配的内存的指针,如果不为其提供重载的赋值操作符和复制的构造函数会造成逻辑错误。

防止类对象被复制是能够实现的,只须令重载的赋值操作符和复制构造函数为private即可。

对于下标运算符的处理,数组中相应下标的元素作为引用返回,以便能够作为左值,而在常量版本的情况下返回右值。

(5)类型转换

a. 实现用户自定义类型和内部类型之间的转换,这种转换可以用转换构造函数实现,使用带单个参数的构造函数,这种函数的功能是将其他类型的对象转换为某个特定类的对象。

b.转换操作符,把一种类的对象转换成其他类的对象或者内部类型的对象,强制类型转换操作符只能够是非静态的成员函数,不能够是友元函数

如A::operator char *() const;

声明了一个重载的强制类型转换操作符函数,根据用户自定义类型A的对象建立一个临时的char*类型的对象,如果s是某个类的对象,编译器遇到表达式char*时,会产生函数调用s.operator char*(),操作数是调用成员函数operator char*的类对象s

强制类型转换操作符和转换构造函数在需要的时候,编译器会自动地调用这些函数建立临时对象

cout<<s

编译器会调用强制类型转换操作符函数operator char*将对象转换为char*,并在表达式中使用char*的结果,任何只含一个参数的构造函数都可以认为是一种转换构造函数,转换构造函数意味不必为将字符串赋值给String类型对象提供重载的赋值操作符,编译器会调用该函数建立包含该字符串的一个临时String对象,然后调用重载的赋值操作符将临时String对象赋给另外一个String对象

c.函数调用操作符

String operator()(int,int)

重载函数调用操作符,重载函数调用操作符只能是非静态成员函数,只有当“函数名”是String类型的对象时,才能使用该操作符。

String String::operator()(int index,int subLength)
{
assert(index>=0&&index<length&&subLength>=0);
int len;
if(subLength==0 || length-index<subLength)
{
len=length-index;
}
else{
len=subLength;
}

char *tempPtr=new char[len+1];
assert(tempPtr!=0);
strncpy(tempPtr,&sPtr[index],len);
tempPtr[len]='/0';

String tempString(tempPtr);
delete[] tempPtr;
return tempString;
}


该函数的定义实例如下,调用方式为,s(4,10),其中s为String类型的对象。

d.重载 ++和--

重载前置的方法与重载其他前置一元操作符一样

编译器在执行++dl时,会生成成员函数调用dl.operator++()

该函数原型为Date&operator++()

friend Date&operator++(Date &)

编译器在遇到后置自增表达式d++时,生成成员函数调用dl.operator(0)

该函数的函数原型为Date operator++(int)

friend Date operator++(Date &,int)

0只是一个伪值,使操作符函数operator ++在用于后置自增操作和前置自增操作时的参数表有所区别

前置使对象值增加1,返回的是自增后的对象,后置也是对象值增加1,返回的是自增前的值。

试比较如下两段代码

前置

Date &Date::operator ++()
{
helpIncrement();
return *this;
}


后置

Date Date::operator ++(int)
{
Date temp=*this;
helpIncrement();
return temp;
}


对象都执行了自增操作,不同的是,前者返回的是执行自增操作之后的对象,后者保存自增之前的值,并返回自增之前的值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: