再看 运算符重载

运算符重载:分为 全局函数重载 和 成员函数重载两种:

1:重载输入输出操作符:

第一版:全局函数重载:

// 运算符重载3.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <ostream>
using namespace std;
class Test
{
public:
	int i;
	int j;

	Test()
	{
		i = j = 0;
	}
	Test(int a, int b)
	{
		i = a;
		j = b;
	}
};

ostream& operator <<(ostream& out,const Test& t)
{
	out<<t.i<<" "<<t.j<<endl;
	return out;
}

istream& operator >>(istream& in, Test& t)
{
	in>>t.i>>t.j;
	if (in.fail())
	{
		cout<<"error"<<endl;
	}
	return in;
}
int _tmain(int argc, _TCHAR* argv[])
{
	Test t0;
	Test t1(3,4);
	cout<<t0<<t1<<endl;

	Test t3;
	cin>>t3;
	cout<<t3;
	getchar();
	return 0;
}

第二版:友元全局函数重载:

// 运算符重载3.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <ostream>
using namespace std;

class Test
{
private: //注意这里为private
	int i;
	int j;
public:
	Test()
	{
		i = j = 0;
	}
	Test(int a, int b)
	{
		i = a;
		j = b;
	}
	//严格的来说,运算符重载跟友元函数没有任何关系,之所以要用友元函数,是因为这里的i跟j是private类型,我们编写<< and >>的重载
	//本来应该是属于全局变量函数重载的方式,但是全局函数没有访问i和j的权限,所以这里就声明为友元函数,使得可以访问i和j
	//看网上有人说:运算符重载分为:成员函数重载与友元函数重载,严格来说不可以这么说,应该分为全局函数重载与成员函数重载
	friend ostream& operator<<(ostream& out,const Test& t);
	friend istream& operator >>(istream& in, Test& t);
	//istream& operator >>(istream& in, Test& t);//ERROR  参数过多
};

//<< 与 >>运算符重载不可以为成员函数重载,因为那样相当于会有三个参数(还有一个this)
ostream& operator <<(ostream& out,const Test& t)
{
	out<<t.i<<" "<<t.j<<endl;
	return out;
}
istream& operator >>(istream& in, Test& t)
{
	in>>t.i>>t.j;
	if (in.fail())
	{
		cout<<"error"<<endl;
	}
	return in;
}

int _tmain(int argc, _TCHAR* argv[])
{
	Test t0;
	Test t1(3,4);
	cout<<t0<<t1<<endl;

	Test t3;
	cin>>t3;
	cout<<t3;
	getchar();
	return 0;
}

//严格的来说,运算符重载跟友元函数没有任何关系,之所以要用友元函数,是因为这里的i跟j是private类型,我们编写<< and >>的重载

//本来应该是属于全局变量函数重载的方式,但是全局函数没有访问i和j的权限,所以这里就声明为友元函数,使得可以访问i和j

//看网上有人说:运算符重载分为:成员函数重载与友元函数重载,严格来说不可以这么说,应该分为全局函数重载与成员函数重载

2:成员函数重载与全局函数重载的区别:

// 运算符重载.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
using namespace std;

class CComplex
{
public:
	double m_i;//
	double m_j;//
	CComplex()
	{
		m_i = m_j;
	}
	CComplex(double m_i, double m_j = 0)
	{
		this->m_i = m_i;
		this->m_j = m_j;
	}
	//成员函数重载;在类的内部实现单目运算是无参函数
	CComplex operator-()
	{
		CComplex t(this->m_i,this->m_j);
		t.m_i *= -1;
		t.m_j *= -1;
		return t;
	}
	//类的内部实现双目运算符是1个参数,只要带入右值,左值用this代替
	/*
	CComplex operator+(const CComplex rhs)
	{
		CComplex t;
		t.m_i = m_i + rhs.m_i;
		t.m_j = m_j + rhs.m_j;
		return t;
	}
	*/

	//成员函数运算符重载*
	const CComplex operator*(const CComplex& rhs)
	{
		CComplex t;
		t.m_i = this->m_i * rhs.m_i;
		t.m_j = this->m_j * rhs.m_j;

		return t;
	}

	void Print()
	{
		cout<<"输出:"<<m_i<<" "<<m_j<<endl;
	}
};

/*
//负号重载,并不是想改变里面参数的数据,比如i为3,并不是要把这里的i改为-3;只是在它前面加上负号;所以返回一个临时对象
CComplex operator-(const CComplex& c)//如果在函数里面不会改变参数本身,那么就设置为const参数
{
	//如果返回的是一个临时对象,那么函数返回值就是返回对象,不能返回引用(临时对象在函数结束会销毁,引用成为了孤立的)
	CComplex t = c;
	t.m_i = t.m_i * (-1);
	t.m_j = (-1) * t.m_j;
	return t;
}
*/
CComplex operator+( const CComplex& c1,  const CComplex& c2)
{
	CComplex c;
	c.m_i = c1.m_i + c2.m_i;
	c.m_j = c1.m_j + c2.m_j;

	return c;
}

//全局函数
CComplex operator *(const CComplex& lhs,const CComplex& rhs)
{
	CComplex t;
	t.m_i = lhs.m_i * rhs.m_i;
	t.m_j = lhs.m_j * rhs.m_j;
	return t;
}
int _tmain(int argc, _TCHAR* argv[])
{
	/*
	CComplex test(33,44);
	(-test).Print();
	//-test.Print(); ERROR 符号优先级的原因
	CComplex test2;
	test2 = -test;
	test2.Print();
	*/

	/************************************************************************/
	/*operator+                                                             */
	/************************************************************************/
	CComplex c1(2,3);
	CComplex c2(3,4);
	CComplex c3 = 1+c2;
	c3.Print();

	/************************************************************************/
	/*operator*                                                             */
	/************************************************************************/

	CComplex a1(1,2);
	CComplex a2(2,3);
	CComplex a3 = a1 * a2;
	CComplex a4 = a1.operator*(a2);
	a4.Print();
	a3.Print();

	/************************************************************************/
	/*operaotr*                                                             */
	/************************************************************************/

	CComplex a5 = a1 * 3;//3 会隐式转换,这里调用的是成员函数的operator*
	a5.Print();
	//CComplex a6 = 4*a1; //error C2677: 二进制“*”: 没有找到接受“CComplex”类型的全局运算符(或没有可接受的转换)
	//a6.Print(); 

	//如果有全局的operator*  则可以编译通过;
	CComplex a6 = 4*a1;
	a6.Print();

	/************************************************************************/
	/* operator* 要声明为CComplex的友元函数吗?不需要,全局函数就可以实现要求*/
	/************************************************************************/
	getchar();
	return 0;
}

3:前置++与后置++的操作符重载

// 运算符重载2.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
using namespace std;

class CComplex
{
public:
	CComplex()
	{
		m_i = m_j = 0;
	}
	CComplex(int a, int b = 0)
	{
		m_j = b;
		m_i = a;
	}
	void Print()
	{
		cout<<"m_i = "<<m_i<<" "<<"m_j = "<<m_j<<endl;
	}

	/*
	CComplex operator ++(int)
	{
		CComplex ret(*this);

		this->m_j++;
		this->m_i++;

		return ret;
	}

	CComplex& operator ++()
	{
		++m_i;
		++m_j;

		return *this;
	}
	*/

public:
	int m_i;
	int m_j;
};

//前置++
CComplex& operator++(CComplex& c) //1:返回的是参数本身,所以函数返回对象的引用,2:因为在函数当真会修改参数的值,所以参数不是const
{
	++c.m_i;
	++c.m_j;

	return c;

}

//后置++
CComplex operator++(CComplex& c,int)//1:这里返回一个临时变量,所以返回值只能是对象,不能是引用;2:因为在函数当真会修改参数的值,所以参数不是const
{
	CComplex ret = c;

	++c.m_i;
	++c.m_j;

	return ret;
}

int _tmain(int argc, _TCHAR* argv[])
{

	CComplex c1(1,3);
	++c1;
	c1.Print();

	CComplex c2(1,3);
	CComplex c3 = c2++;
	c3.Print();

	getchar();
	return 0;
}

前置++与后置++通过一个占位参数来区别

4:重载赋值操作符以及为什么复制操作符必须重载为成员函数

// 运算符重载4.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
using namespace std;

class MyCArry
{
private:
	int  m_iLength;
	int* m_pSpace;

public:
	MyCArry()
	{
		m_iLength = 0;
		m_pSpace  = NULL;
	}
	MyCArry(int length)
	{
		if (length < 0)
		{
			length = 0;
		}
		m_iLength = length;
		m_pSpace = new int(length);

	}
	MyCArry(const MyCArry& test)
	{
		m_iLength = test.m_iLength;
		m_pSpace  = new int(m_iLength);
		for(int i = 0; i < m_iLength; i++)
	    {
			   m_pSpace[i] = test.m_pSpace[i];
		}  

	}

	/*
	MyCArry& operator = (const MyCArry& rhs)
	{
		//注意要有自赋值检查
		if ( this == &rhs)
		{
			*this = rhs;
		}
		this->m_iLength = rhs.m_iLength;
		delete m_pSpace;
		m_pSpace = new int(m_iLength);
		for(int i = 0; i < m_iLength; i++)
		{
			m_pSpace[i] = rhs.m_pSpace[i];
		}  

		return *this;
	}
	*/
	//改进版本防止出现异常的时候内存泄漏
	MyCArry& operator = (const MyCArry& rhs)
	{
		int* pOrig = m_pSpace;//记住原先的m_Space;
		this->m_iLength = rhs.m_iLength;
		m_pSpace = new int(m_iLength);
		for(int i = 0; i < m_iLength; i++)
		{
			m_pSpace[i] = rhs.m_pSpace[i];
		}
		delete pOrig;//删除原先的m_pSpace
		return *this;
	}

};
//MyCArry& operator = (const MyCArry& rhs)  这里会报错误:operator = 必须是成员函数
//对于赋值操作符(=)--比较特别,因为任何类如果不提供显示的拷贝赋值(即重载=),则编译器会隐式地提供一个。
//这样的话,如果你再通过友元声明,进行全局的定义会造成调用二义性(即使允许,编译也会出错)。
//=,[],(),->  只能声明为成员函数,是为了避免不合法的书写编译通过(推测。。。。。)(出现1=;1[],1(),1->;等这些格式)
//其实这里+ ,+=等操作符,为类成员函数的时候: 1+a;编译不通过;为全局成员函数的时候:1+a;编译通过
//所以这里不用纠结为什么= [] () ->不能重载为全局函数,就是C++的规定,至于为什么,实在是找不到原因,找到的也都没有说服力
int _tmain(int argc, _TCHAR* argv[])
{

	MyCArry t1(5);
	MyCArry t2;
	t2 = t1;

	return 0;
}

/*
在实际开发过程中,单目运算符建议重载为成员函数,而双目运算符建议重载为友元函数。
通常下双目运算符重载为友元函数比重载为成员函数更方便,但是有时双目运算符必须重载为成员函数,
例如赋值运算符=。还有如果需要修改对象内部的状态,一般可以选择利用类成员函数进行修改。

*/

//MyCArry& operator = (const MyCArry& rhs) 这里会报错误:operator = 必须是成员函数//对于赋值操作符(=)--比较特别,因为任何类如果不提供显示的拷贝赋值(即重载=),则编译器会隐式地提供一个。

//这样的话,如果你再通过友元声明,进行全局的定义会造成调用二义性(即使允许,编译也会出错)。

//=,[],(),-> 只能声明为成员函数,是为了避免不合法的书写编译通过(推测。。。。。)(出现1=;1[],1(),1->;等这些格式)

//其实这里+ ,+=等操作符,为类成员函数的时候: 1+a;编译不通过;为全局成员函数的时候:1+a;编译通过

//所以这里不用纠结为什么= [] () ->不能重载为全局函数,就是C++的规定,至于为什么,实在是找不到原因,找到的也都没有说服力

总结:

要选择哪种方式重载也没有一个确切的说法,要看具体情况,例如+,+=,是双目运算符,重载为成原函数可以避免出现 1 = a;1 +=a;这样的格式,但是*乘法具有

交换率,  1*a; a*1;这两种格式都要支持,那么用全局函数重载好;

记住 << >>必须是全局函数重载

记住 = [] () ->必须是成员函数重载

其余的都看具体情况

时间: 2024-08-05 16:44:06

再看 运算符重载的相关文章

新标准C++程序设计读书笔记_运算符重载

形式 返回值类型 operator 运算符(形参表) { …… } 运算符重载 (1)运算符重载的实质是函数重载(2)可以重载为普通函数,也可以重载为成员函数 1 class Complex 2 { 3 public: 4 double real,imag; 5 Complex( double r = 0.0, double i= 0.0 ):real(r),imag(i) { } 6 Complex operator-(const Complex & c); 7 }; 8 9 Complex

看懂Gradle脚本(4)- Groovy语法之运算符重载

继续讨论Task定义 回想一下前一篇文章的样例: task myTask { doLast { println 'hello world!' } } 这段脚本定义了一个名为myTask的任务.而且通过一个闭包对这个任务进行了配置,给它加了一个Action.doLast是Task的一个方法. 由于这样的情况非经常见,所以Gradle提供了一种更加方便的写法,例如以下所看到的: task myTask << { println 'hello world!' } 能够少写两行代码 :) AST转换

Kotlin中复合赋值(+=,-=,……)运算符重载

本篇建立在已经了解了kotlin中运算符重载的理念以及如何实现的基础上. 来我们首先写一个简单的类,然后重载运算符+,+=,-,-=这个几个运算符.代码如下: data class Point(var x: Int, var y: Int) { operator fun plus(point: Point): Point { return Point(this.x + point.x, this.y + point.y) } operator fun plusAssign(point: Poin

[转]C++之运算符重载(2)

上一节主要讲解了C++里运算符重载函数,在看了单目运算符(++)重载的示例后,也许有些朋友会问这样的问题.++自增运算符在C或C++中既可以放在操作数之前,也可以放在操作数之后,但是前置和后置的作用又是完全不同的(q前置运算符:先加1,再赋值:后置运算符:先赋值,再加1).那么要怎么重载它们,才可以有效的区分开来呢?今天我就来说说C++中是怎么处理前置运算符和后置运算符的重载的.以及介绍一下插入运算符(>>)和提取运算符(<<)的重载. 1.在C++里编译器是根据运算符重载函数参数

Python——运算符重载(1)

运算符重载 关键概念: 1.运算符重载让类拦截常规的Python运算. 2.类可重载所有的Python表达式运算符. 3.类也可重载打印.函数调用.属性点号运算等内置运算. 4.重载使类实例的行为像内置类型. 5.重载是通过特殊名称的类方法来实现的. 运算符重载只是意味着在类方法中拦截内置的操作--当类的实例出现在内置操作中,Python自动调用你的方法,并且你的方法的返回值变成了相应操作的结果. =================================================

C++之运算符重载(2)

上一节主要讲解了C++里运算符重载函数,在看了单目运算符(++)重载的示例后,也许有些朋友会问这样的问题.++自增运算符在C或C++中既可以放在操作数之前,也可以放在操作数之后,但是前置和后置的作用又是完全不同的(q前置运算符:先加1,再赋值:后置运算符:先赋值,再加1).那么要怎么重载它们,才可以有效的区分开来呢?今天我就来说说C++中是怎么处理前置运算符和后置运算符的重载的.以及介绍一下插入运算符(>>)和提取运算符(<<)的重载. 1.在C++里编译器是根据运算符重载函数参数

Swift教程之运算符重载

原文地址:http://www.raywenderlich.com/80818/operator-overloading-in-swift-tutorial 作者:Corinne Krych  译者:孟祥月 blog:http://blog.csdn.net/mengxiangyue 这篇文章是本人第一次翻译,难免有错误,翻译的时候使用的是txt,所以格式上面有些不太好. 在早前的IOS 8盛宴系列的教程里,你已经了解到,Swift提供了许多强大的.现代的编程特性,比如泛型.函数式编程.一等类型

细谈C++的运算符重载

什么是运算符重载? 顾名思义就是将原本的操作符以我们的方式定义出来,方便我们使用. 为什么要进行运算符重载? 简单的理由就是将减少程序员的工作量,首先先看一个简单的例子: class A{ public:     A(int data):data(data){};     void show(){         cout << "data = " << data << endl;     }    private:     int data; };

Python 3 之 运算符重载详解

基础知识 实际上,"运算符重载"只是意味着在类方法中拦截内置的操作--当类的实例出现在内置操作中,Python自动调用你的方法,并且你的方法的返回值变成了相应操作的结果.以下是对重载的关键概念的复习: 运算符重载让类拦截常规的Python运算. 类可重载所有Python表达式运算符 类可以重载打印.函数调用.属性点号运算等内置运算 重载使类实例的行为像内置类型. 重载是通过特殊名称的类方法来实现的. 换句话说,当类中提供了某个特殊名称的方法,在该类的实例出现在它们相关的表达式时,Pyt