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

C++学习笔记14 操作符的重载

2014-08-31 11:57 483 查看
C++中操作符重载的本质

 C++中通过operator关键字可以利用函数扩展操作符

 operator的本质是通过函数重载实现操作符重载



C++中的类的友元

 private声明使得类的成员不能被外界访问

 但是通过friend关键字可以例外的开放权限



#include <iostream>
#include <cstdio> 

/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;

class complex
{
	

	int a;
	int b;
	
	public:
		complex(int a = 0, int b= 0)//默认参数的使用 
		{
			this->a = a;
			this->b = b;
		}
		int getA()
		{
			return a;
		}
		int getB()
		{
			return b;
		}
     //friend complex operator + (const complex& c1,const complex& c2 );
     complex operator+(const complex& c); 
     friend ostream& operator<<(ostream& out, const complex& c);	
};
ostream& operator<<(ostream& out, const complex& c )
{
	out<<c.a<<"+"<<c.b<<"i"<<endl;
	return out;
}
/*
complex operator+(const complex& c1,const complex& c2) 
{
		complex ret;
		ret.a = c1.a+c2.a;
		ret.b = c1.b+c2.b;
		
		return ret;
} 
*/
complex complex::operator +(const complex& c)
{
	complex ret;
	ret.a = this->a + c.a;
	ret.b= this->b + c.b;
	
	return ret;
}
int main(int argc, char** argv) {
	
	complex c1(1,2);
	complex c2(3,4);
	complex c3 = c1+c2;
	
	cout<<c1<<endl;
	cout<<c2<<endl;
	cout<<c3<<endl;
	//cout<<c3.a<<endl;
	//cout<<c3.b<<endl; //main不是complex类的朋友,无法访问a和b 
	
	
	cin.get();
	printf("HelloWorld\n");
	printf("press any key............\n");
	return 0;
}


操作符重载是C++的强大特性之一

 操作符重载的本质是通过函数扩展操作符的语义

 operator关键字是操作符重载的关键

 friend关键字可以对函数或类开发访问权限

 操作符重载遵循函数重载的规则



通过operator关键字能够将操作符定义为全局函数

操作符重载的本质就是函数重载

类的成员函数是否可以作

为操作符重载的函数?



用成员函数重载的操作符

比全局操作符重载函数少一个参数,即左操作数

不需要使用friend关键字



操作符重载

当无法修改左操作数的类时,使用全局函数进行重载

=, [], ()和->操作符只能通过成员函数进行重载



下面以一个Array类进行分析操作符的重载:

#ifndef _ARRAY_H_ 
#define _ARRAY_H_

class Array
{
	private:
		int mLength;
		int *mSpace;
		
	public:
		Array(int length);
		Array(const Array& obj);
		int length();
		//void setData(int index, int value);
		//int getData(int index);
	    ~Array(); 
		int& operator [](int i);//下标操作符的重载
		const int& operator[](int i) const; //下标操作符的常量版本 
		
		Array& operator = ( const Array& obj);//重载赋值操作符,定义为引用,是返回结果要作为左值,可以进行 a1 = a2 = a3 的连续赋值
		bool operator == (const Array& obj); //重载等于操作符 
		bool operator !=(const Array& obj);  //重载不等于操作符 
		 
};
#endif

<span style="color:#ff0000;">/*

重载【】下标操作符: 

重载下标操作符 [] 是很常见的事情,它保存类中元素的顺序。vector 和 string 就是这样的类。在重载操作符[]时,一定要记住定义它的两个版本:非常量版本和常量版本:

int& operator [](int i);//下标操作符的重载
const int& operator[](int i) const; //下标操作符的常量版本 

重载=赋值操作符:
 
Array& operator = ( const Array& obj);//重载赋值操作符,定义为引用,是返回结果要作为左值,可以进行 a1 = a2 = a3 的连续赋值
注意:
考虑自赋值的情况:
    /* 
	我们需要记住的第二点微妙之处是,两个对象都已经拥有为字符串分配的内存,
	因此赋值运算符函数必须做的第一件事是删除分配给第一个对象的内存,
	然后重新分配足够的内存,以容纳属于第二个对象的字符串。做完这件事之后,
	就可以将来自第二个对象的字符串复制到第一个对象现在拥有的内存中。

	a2 = a1; //首先删除为a2分配的内存,然后重新分配内存,以容纳第二个对象的内容。 
	
	这时  如果  a2 = a2;悲剧了,a2拥有的内存已经被释放了 

	a2= a2;

	显然,我们不会直接做任何像这条语句这样愚蠢的事情,但此类现象很容易隐藏在指针的背后,就像下面的语句那样:

	a1 = *p;

	如果指针p指向motto1,那么实质上这就是前面那条赋值语句。这种情况下,目前的赋值运算符函数将释放供a1使用的内存,
	然后基于已经被删除的字符串的长度另外分配一些内存,并试图复制当时很可能已经被破坏的旧内存。
	通过在函数的开始部分检查左右操作数是否相同,我们就可以修正上述问题,

 Array& Array::operator=( const Array& obj)
{
	if(this != &obj)//判断是否是自身赋值 
	{
	
		mLength = obj.mLength;
		
		mSpace = new int[mLength]; //自己申请一个新的空间出来,而系统提供的就是指向同一个空间。 
		
		for(int i = 0; i < mLength; i++)
		{
			mSpace[i] = obj.mSpace[i];
		}
	}
	

	return *this;
}

重载等于不等于操作符:
bool operator == (const Array& obj); //重载等于操作符 
bool operator !=(const Array& obj);  //重载不等于操作符 
可以定义为全局重载函数,也可以定义为成员函数重载操作符

</span>*/




#include "Array.h"

Array::Array(int length)
{
	if(length < 0)
	length = 0;
	mLength = length;
	mSpace = new int[mLength];
}

Array::Array(const Array& obj)
{
	mLength = obj.mLength;
	mSpace = new int[mLength]; //自己申请一个新的空间出来,而系统提供的就是指向同一个空间。 
	for(int i = 0; i < mLength; i++)
	{
		mSpace[i] = obj.mSpace[i];
	}
}
int Array::length()
{
	return mLength;
} 
/*
void Array::setData(int index , int value)
{
	mSpace[index] = value;
}

int Array::getData(int index)
{
	return mSpace[index];
}
*/
Array::~Array()
{
	mLength = -1;
	delete[] mSpace;
}
int& Array::operator [](int i)
{
	return mSpace[i];
	
}
const int&  Array::operator[](int i) const
{
	return mSpace[i];
}

Array& Array::operator=( const Array& obj)
{
	if(this != &obj)
	{
	
		mLength = obj.mLength;
		
		mSpace = new int[mLength]; //自己申请一个新的空间出来,而系统提供的就是指向同一个空间。 
		
		for(int i = 0; i < mLength; i++)
		{
			mSpace[i] = obj.mSpace[i];
		}
	}
	

	return *this;
}

bool Array::operator == (const Array& obj)
{
	bool ret = true;
	if(this->mLength == obj.mLength)
	{
		for(int i = 0; i < mLength; i++)
		{
			if(mSpace[i] = obj.mSpace[i])
			{
			}
			else
			{
				ret = false;
				break;
			}
		}
	}
	else
	{
		ret = false;
	}
}

bool Array::operator !=(const Array& obj)
{
	return !(*this == obj);
}




#include <iostream>
#include <stdio.h>
#include "Array.h"

int main(int argc, char** argv) {
	
	
	Array a1(10);
	
	
	a1[2] = 9;
	a1.operator [](5) = 10;//调用下标操作符函数形式 

	for(int i = 0 ;i < a1.length(); i++)
	{
		 a1[i] = i;
	}
	
	
	for( int i = 0; i< a1.length(); i++)
	{
		printf("Element i = %d\n", a1[i]);
		
	}
	
	printf("**********************************\n");
	
	Array a2(5);
	
	if(a1 == a2)
	{
		printf("a1 == a2\n");
	}
	/*
	<span style="color:#ff0000;">我们需要记住的第二点微妙之处是,两个对象都已经拥有为字符串分配的内存,
	因此赋值运算符函数必须做的第一件事是删除分配给第一个对象的内存,
	然后重新分配足够的内存,以容纳属于第二个对象的字符串。做完这件事之后,
	就可以将来自第二个对象的字符串复制到第一个对象现在拥有的内存中。

</span>	*/

	a2 = a1; //<span style="color:#ff0000;">首先删除为a2分配的内存,然后重新分配内存,以容纳第二个对象的内容。 
</span>	
 	if(a1 == a2)
	{
		printf("a1 == a2\n");
	}
	for( int i = 0; i< a2.length(); i++)
	{
		printf("Element i = %d\n",a2[i]);
	}
	
	
	
	/*
	a1.destory();
	
	a2.destory();
	//如果这里才用系统提供的默认的拷贝构造函数,它只是进行单纯的复制工作,所以a1与a2中的mSpace指向的是
	//同一个空间,当执行a1.destory之后,这片空间已经释放,执行a2.destory就会出错 
	*/
	
	getchar(); 
	return 0;
}


C++编译器会为每个类提供默认的赋值操作符

默认的赋值操作符只是做简单的值复制

类中存在指针成员变量时就需要重载赋值操作符



++操作符的重载

++操作符只有一个操作数

++操作符有前缀和有后缀的区分

C++中通过一个占位参数来区分前置运算和后置运算

#include <iostream>
#include <cstdio> 

/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;

class complex
{
	

	int a;
	int b;
	
	public:
		complex(int a = 0, int b= 0)//默认参数的使用 
		{
			this->a = a;
			this->b = b;
		}
		int getA()
		{
			return a;
		}
		int getB()
		{
			return b;
		}
     complex operator+(const complex& c);
     friend ostream& operator<<(ostream& out, const complex& c);	
     complex operator++(int );
     complex& operator++(); 
};
ostream& operator<<(ostream& out, const complex& c )
{
	out<<c.a<<"+"<<c.b<<"i"<<endl;
	return out;
}

complex complex::operator +(const complex& c)
{
	complex ret;
	ret.a = this->a + c.a;
	ret.b= this->b + c.b;
	
	return ret;
}

complex complex::operator ++(int) //后置++ 后置++,返回临时对象ret的值,只能作为右值,不能被赋值。

{
	complex ret = *this;
	a++;
	b++;
	
	return ret;
	
}

complex& complex::operator ++() //前置++ 前置++,返回this对象的引用,可以作为左值,被赋值。
{
	
	++a;
	++b;
	
	return *this;
	
}
int main(int argc, char** argv) {
	
	complex c1(1,2);
	complex c2(3,4);
	complex c3 = c2;
	
	c2++;
//	++c3;  //前置++ 的效率又高一点
	c3 = c2++; 
	
	c2.operator ++(0);
	
	 
	cout<<c1<<endl;
	cout<<c2<<endl;
	cout<<c3<<endl;
		
	cin.get();
	printf("HelloWorld\n");
	printf("press any key............\n");
	return 0;
}




还有 不要重载&&与||,因为重载后违背了它原先的短路规则(优先级)

#include <cstdio>
#include <iostream>

using namespace std;

class Test
{
	int i;
	public:
		Test( int i)
		{
			this->i = i;	
		}
		Test operator+(const Test& obj)
		{
			Test ret(0);
			cout<<"Test operator+(const Test& obj)"<<endl;
			
			return ret;
		}
	    bool operator&& (const Test& obj)
		{
			cout<<"bool operator&& (const Test& obj)"<<endl;
			return i&&obj.i;
		}
};

int main()
{
	int a1 = 0;
	int a2 = 1;
	
	if(a1 && (a1+a2))
	{
		cout<<"Hello World"<<endl;
	}
	
	
	Test t1 = 0;
	Test t2 = 0;
	
	if(t1 && (t1+ t2))
	{
		cout<<"Hello World 2222"<<endl;
	}
	//上面违背了 &&
	//t1.operator(t1.operator(t2))等价于 t1 && (t1+ t2)  显然先执行  t1+t2 
	cin.get();
}

//最好不要重载&& 与 || 因为它完全违背了 它原来的短路规则
//&&和||内置实现了短路规则
//操作符重载是靠函数重载来完成的
//操作数作为函数参数传递
//C++的函数参数都会被求值,无法实现短路规则
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: