C++运算符重载详解

为什么要对运算符进行重载:

C++预定义中的运算符的操作对象只局限于基本的内置数据类型,但是对于我们自定义的类型(类)是没有办法操作的。但是大多时候我们需要对我们定义的类型进行类似的运算,这个时候就需要我们对这么运算符进行重新定义,赋予其新的功能,以满足自身的需求。



C++运算符重载的实质:

运算符重载的实质就是函数重载或函数多态。运算符重载是一种形式的C++多态。目的在于让人能够用同名的函数来完成不同的基本操作。要重载运算符,需要使用被称为运算符函数的特殊函数形式,运算符函数形式:operatorp(argument-list)//operator 后面的'p'为要重载的运算符符号。

即:

<返回类型说明符> operator <运算符符号>(<参数表>)
{

     <函数体>

}


一个简单运算符重载实例:

(我在这篇博文里说过了,很简单,刚接触的朋友可以看一下,有代码)

http://blog.csdn.net/lishuzhai/article/details/50764312



运算符重载的规则:

(1)为了防止用户对标准类型进行运算符重载,C++规定重载后的运算符的操作对象必须至少有一个是用户定义的类型

这是什么意思呢?

比如说现在有两个数:int number1,int number2,

那么number1+number2 求的是两个数的和,

但是如果你重载以后让着两个数相加为他们的乘积,这肯定是不合乎逻辑的。

可能重载以后会有二义性,导致程序不知道该执行哪一个(是自带的的还是重载后的函数)


(2)使用运算符不能违法运算符原来的句法规则。如不能将% 重载为一个操作数,

例如:
int index;

%index;这种是不被允许的。


(3)不能修改运算符原先的优先级。


(4)不能创建一个新的运算符,例如不能定义operator** (···)来表示求幂


(5)不能进行重载的运算符:成员运算符,作用域运算符,条件运算符,sizeof运算符,typeid(一个RTTI运算符),const_cast、dynamic_cast、reinterpret_cast、static_cast强制类型转换运算符


(6)大多数运算符可以通过成员函数和非成员函数进行重载但是下面这四种运算符只能通过成函数进行重载:

= 赋值运算符,()函数调用运算符,[ ]下标运算符,->通过指针访问类成员的运算符。


(7)除了上述的规则,其实我们还应该注意在重载运算符的时候遵守一些明智的规则:例如:不要将+运算符重载为交换两个对象的值。


重载运算符的两种形式:
重载运算符有两种方式,即:

重载为类的成员函数||重载为类的非成员函数。

重载为类的非成员函数的时候:

通常我们都将其声明为友元函数,因为大多数时候重载运算符要访问类的私有数据,(当然也可以设置为非友元非类的成员函数。但是非友元又不是类的成员函数是没有办法直接访问类的私有数据的),如果不声明为类的友元函数,而是通过在此函数中调用类的公有函数来访问私有数据会降低性能。所以一般都会设置为类的友元函数,这样我们就可以在此非成员函数中访问类中的数据了。


下面我同意讲解一个经典的例子来说明一下这两者的区别、特点、要重载时可能出现的问题

GO:

现在我们有一个时间类,我们要进行的任务是对时间类的加减乘。或许你会觉得很简单,简单就对了,因为这更容易让你深入的理解到这二者


MyTime.h文件:
#pragma once
#ifndef MYTIME_H_
#define MYTIME_H_
class CMyTime
{
private:
int m_hours;
int m_minutes;
public:
CMyTime();
CMyTime(int h, int m = 0);
void AddHr(int h);  //小时更改
void AddMin(int m);//分钟更改
void Reset(int h = 0, int m = 0);  //重新设置时间
CMyTime operator+(const CMyTime &t) const;  //重载加法
CMyTime operator-(const CMyTime &t) const;  //重载减法
CMyTime operator*(double n) const;                //重载乘法
void Show() const;
~CMyTime();
};
#endif


MyTIme.cpp文件:
#include "stdafx.h"
#include "MyTime.h"
#include <iostream>

CMyTime::CMyTime()
{
m_hours = 0;
m_minutes = 0;
}


CMyTime::CMyTime(int h, int m)
{
m_hours = h;
m_minutes = m;
}


CMyTime::~CMyTime()
{
}


void CMyTime::AddHr(int h)                                             //小时更改
{
m_hours += h;
}


void CMyTime::AddMin(int m)                                             //分钟更改
{
m_minutes = m;
}


void CMyTime::Reset(int h, int m)                                           //重新设置时间
{
m_hours = h;
m_minutes = m;
}


CMyTime CMyTime::operator+(const CMyTime &t) const              //重载加法运算符函数
{
CMyTime sum;
sum.m_minutes = t.m_minutes + m_minutes;
sum.m_hours = t.m_hours + m_hours + sum.m_minutes / 60;
sum.m_minutes %= 60;
return sum;
}


CMyTime CMyTime::operator-(const CMyTime &t) const         //重载为减法运算符函数
{
CMyTime diff;
int tot1, tot2;
tot1 = t.m_minutes + 60 * t.m_hours;
tot2 = m_minutes + 60 * t.m_hours;
diff.m_minutes = (tot2 - tot1) % 60;
diff.m_hours = (tot2 - tot1) / 60;
return diff;
}


CMyTime CMyTime::operator*(double n) const                 //重载为乘法运算符函数。
{
CMyTime result;
long totalMinutes = m_hours * 60 * n+ m_minutes *n;
result.m_minutes = totalMinutes % 60;
result.m_hours = totalMinutes / 60;
return result;
}


void CMyTime::Show() const
{
std::cout << m_hours << " hours "
<< m_minutes << " minutes\n";
}


主函数:

// Study11-02.cpp : 定义控制台应用程序的入口点。
//


#include "stdafx.h"
#include <iostream>
#include "MyTime.h"


int _tmain(int argc, _TCHAR* argv[])
{
using std::cout;
using std::endl;
CMyTime weeding(4, 35);
CMyTime waxing(2, 47);
CMyTime total;
CMyTime diff;
CMyTime adjusted;


cout << "weeding Time = ";
weeding.Show();
cout << endl;


cout << "waxing Time = ";
waxing.Show();
cout << endl;


cout << "total work Time = ";   //(1)
total = weeding + waxing;
total.Show();
cout << endl;


diff = weeding - waxing;
cout << "weeding Time - waxing Time = "; //(2)
diff.Show();
cout << endl;


adjusted = total *1.5;                     //(3)
cout << "adjusted work Time = ";
adjusted.Show();
cout << endl;


return 0;
}



在主函数中,我们创建了weeding 和waxing连个对象,进行了操作。那么现在问题来了。

我们看(3)标记处,现在adjusted = total *1.5;  是可以运行的,那么adjusted = 1.5*total可以运行吗?

答案当然是不可以,我们还原下此语句:adjusted = total.operator*(1.5),换成1.5在乘号前面当然是不可以的。因为1.5不是对象。



这个时候有两种解决方式:

一:告诉每个人只能按照adjusted = total *1.5;  这种方式来,这种方式看起来当然是欠缺的。那么重头戏来了:

二:非成员函数,声明为 CMyTime operator *(double m,const CMyTime &t);

这样等价于:A = operator *(1.5,B)等价于:A = 1.5 * B;出于性能考虑,我们将其声明为friend 即友元函数(前面已经解释过为什么要声明为友元了):

friend CMyTime operator*(double m,const CMyTime &t);


接下来我们这样做:在上面的MyTIme.h文件的public中加入:friend CMyTime operator*(double m, const CMyTime &t){ return t*m; }  (因为这个函数操作很简单,可以直接为内联函数);然后就可以运行 A  = 1.5 * B语句了。


接下来我们为了更好的了解重载运算符,来进行<<运算符的重载:

现在我们想让 cout<<adjusted; 这句话能直接执行输出,(显然这种输出方式如果不重载<<运算符是没有办法执行的。因为cout根本不知道输出adjusted的什么东西),对于<<的重载我们有两种版本:

第一种声明为成员函数

照葫芦画瓢:CMyTime operator<<(ostream &s); 那么这种生命方式会造成什么结果呢?答案是:输出会变成:adjusted<< cout;或许看起来很不好,但这确实是正确的。因为这等价于:adjusted.operator<<(cout);

第二种版本:(也是更好的版本):MyTIme.h 声明:friend void operator<<(ostream &os,const CMyTime &t);     MyTime.cpp实现:  void operator<<(ostream &os,const CMyTime &t){os << t.hours << t.minutes};这样就可以执行cout << adjusted 这条语句了。


但是这样会存在一个问题:

我们没有办法执行cout << adjusted <<waxing;这条语句。因为从左往右读(cout << adjusted)<< waxing ,返回类型是void 而我们需要的是waxing的左边是一个ostream对象。我们只需要这样修改:

friend ostream operator<<(ostream &s,const CMyTIme &t); 实现的时候:return &os;就行了。

(程序可以运行,更改也很少,可以试一试的呦);



那么最重要的问题来了,我们什么时候声明为成员函数,什么时候声明为非成员函数呢?

首先,我们要明白这句话:对于成员函数来说,一个操作数通过this指针隐式的传递,(即本身),另一个操作数作为函数的参数显示的传递;对于友元函数(非成员函数)两个操作数都是通过参数来传递的。

(1)一般来说,弹幕运算符重载为类的成员函数,双目运算符重载为类的友元函数(咳咳,一般情况下)

(2)双目运算符不能将 = 。 ()【】。-> 重载为类的友元函数。

(3)如果运算符的第一次操作数要求为隐式转换则必须为友元函数。

(4)当最左边的要求为类对象,而右边的是一个内置类型,则要为友元函数。




最重要的注意点:

T1 = T2 + T3;

可以为T1 = T2.operator+(T3);

也可以为T1  = operator+(T2,T3);

但是这两种方式不能同时声明定义,因为这会出现二义性。造成程序不知道该执行那个函数。在进行运算符重载的时候千万要注意造成二义性的情况。

ok。基本完了。如果你真正搞懂了这篇博文,最基本性的东西就可以了。





C++ 中的面向对象编程允许我们使用类和对象来组织和管理代码。在类中,可以定义成员函数和成员变量。成员函数是与类相关联的函数,它们可以访问类的成员变量并执行与该类相关的操作。成员变量是存储在类中的变量,它们描述了类的状态。 运算符重载C++ 中面向对象编程的一种强大功能。它允许您重新定义运算符以执行特定操作。例如,您可以重载“+”运算符以执行类对象的加法操作。运算符重载使您能够编写更直观和易于使用的代码。 友元函数是类的非成员函数,但它们可以访问类的私有成员。当您需要访问类的私有成员但不想使这些成员成为公共接口的一部分时,友元函数就会很有用。要声明一个友元函数,请在类定义中将其声明为友元。友元函数可以是全局函数或其他类的成员函数。 下面是一个示例类,其中包含运算符重载和友元函数: ```cpp #include <iostream> class MyClass { public: MyClass(int value) : value_(value) {} // 重载加号运算符,将两个 MyClass 对象相加 MyClass operator+(const MyClass& other) { return MyClass(value_ + other.value_); } // 将友元函数声明为 MyClass 的友元 friend void PrintValue(const MyClass& obj); private: int value_; }; // MyClass 的友元函数 void PrintValue(const MyClass& obj) { std::cout << "The value of MyClass is: " << obj.value_ << std::endl; } int main() { MyClass obj1(10); MyClass obj2(20); MyClass result = obj1 + obj2; PrintValue(result); return 0; } ``` 在这个例子中,我们定义了一个 MyClass 类,它包含一个成员变量 value_ 和一个构造函数。我们还重载了加号运算符,以便我们可以将 MyClass 对象相加。最后,我们定义了一个名为 PrintValue 的友元函数,该函数可以访问 MyClass 类的私有成员 value_。 在 main 函数中,我们创建了两个 MyClass 对象,将它们相加并将结果打印到控制台上。请注意,我们使用了友元函数 PrintValue 来打印 MyClass 对象的值,而不是直接访问 MyClass 对象的私有成员。
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值