C++类对象相关的运算符重载

        C++的类对象之间的运算和结构变量一样,对象之间可以用“=”进行赋值,但是不能用“==”,“!=”,“>”,“<”“>=”“<=”进行比较,除非这些运算符经过了“重载”

一、运算符重载基本概念

  运算符重载:就是对已有的运算符 (C++ 中预定义的运算符) 赋予多重的含义,使同一运算符作用于不同类型的数据时导致不同类型的行为。

  通过运算符重载,我们可以扩展C++中提供的运算符的适用范围,是指能够作用于对象。

Tips:

(1)运算符重载的实质是函数重载,可以重载为普通函数,也可以重载为成员函数

(2)把含运算符的表达式转换成对运算符函数的调用。

(3)把运算符的操作数转换成运算符函数的参数。

(4)运算符被多次重载时,根据实参的类型决定调用哪个运算符函数。

基本形式: 返回值类型operator 运算符(形参表){....}

二、运算符的重载

注意事项:

(1)重载为成员函数时,参数个数为运算符目数减一。

(2)重载为普通函数时,参数个数为运算符目数。

运算符重载为友元函数:

  一般情况下 将运算符重载为类的成员函数是比较好的选择

  但有时,我们不得不把运算符重载为普通函数,但是普通函数无法访问类的私有成员,我们就需要把运算符重载为友元函数,使得类的私有成员对该运算符重载函数开放。

Complex c3=c1+c2 本质上是 Complex c3=operator+(c1,c2)

Complex c4=c3-c1 本质上是 Complex c4=operator-(c3,c1)

三、赋值运算符的重载

  有时候希望赋值运算符两边的类型可以不匹配,比如,把一个int类型变量赋值给一个Complex对象,或把一个 char * 类型的字符串赋值给一个字符串对象, 此时就需要重载赋值运算符“=”。

    注意:赋值运算符“=”只能重载为成员函数

  特别地,s1=s2无需重载,因为这是complex类对象=complex类对象

  但是,这样实际上是一种浅拷贝,即会导致s1和s2所指向的内存相同(默认的=是在做指针传递,因为我们定义的str为指针类型),使得你在改变s1的值时也会改变s2的值(反之亦是)。

  因此我们可能需要重载赋值运算符“=”,重新开辟出一块内存区域,来达到深拷贝的目的。

  但是这样可能出现s1=s1,导致报错,所以不妨这样修改:

复制构造函数同样会有深拷贝与浅拷贝的问题,可以这样修改:

四、运算符重载实战练习(可变长整型数组)


#include<iostream>
using namespace std;
class CArray {//可变长数组类
private:
	int size;//保存数组长度
	int* ptr;//指针,指向动态分配的的数组地址
public:
	CArray(int a = 0);//构造函数,表示创建一个长度为a的数组

	CArray(const CArray& a);//复制构造函数,预防浅拷贝

	~CArray();//析构函数,释放内存

	void push_back(int i);//在数组尾部增加一个元素

	int length(){return size;}//返回数组长度

	int &operator[](int i) { return ptr[i]; }//传回的是一个地址
	//返回值不能使用int,因为这样的话不支持a[i]=5

	CArray& operator=(const CArray& a);
	//赋值号的作用是使“=”左边对象里存放的数组,大小和内容都和右边的对象一样
};

CArray::CArray(int a):size(a)
{
	if (a == 0)//如果长度为0,不分配内存
	{
		ptr = NULL;
	}
	else//否则分配对应长度的内存
	{
		ptr = new int[a];
	}
}
CArray::CArray(const CArray& a)
{
	if (a.ptr)//如果传入非空
	{
		ptr = new int[a.size];
		memcpy(ptr, a.ptr, sizeof(int) * a.size);
		size = a.size;
	}
	else//如果传入为空
	{
		ptr = NULL;
		size = 0;
	}
}
CArray::~CArray()
{
	if (ptr)delete[] ptr;
}
void CArray::push_back(int i)
{
	if (ptr)
	{
		int* tmp = new int[size + 1];//创建临时数组
		memcpy(tmp, ptr, sizeof(int) * size);//拷贝原数组至临时数组
		delete[] ptr;//释放原数组
		ptr = tmp;//再令原数组指向临时数组的内存空间
	}
	else
	{
		ptr = new int[1];
	}
	ptr[size] = i;
	size++;
}
CArray& CArray::operator=(const CArray& a)
{
	if (ptr == a.ptr) //防止 a=a 这样的赋值导致出错
	{
		return *this;
	}
	if (a.ptr == NULL)
	{ //如果 a 里面的数组是空的
		if (ptr)//若原数组非空
		{
			delete[] ptr;
			size = 0;
			ptr = NULL;
		}
	}
	else//若a数组非空
	{
		if (ptr)
		{
			delete[] ptr;
			ptr = new int[a.size];
			size = a.size;
			memcpy(ptr, a.ptr, sizeof(int) * a.size);
		}
		else
		{
			ptr = new int[a.size];
			size = a.size;
			memcpy(ptr, a.ptr, sizeof(int) * a.size);
		}
	}
	return *this;
};
int main() { 
	CArray a; //开始里的数组是空的
	for (int i = 0; i < 5; ++i)
		a.push_back(i);
	CArray a2, a3;
	a2 = a;
	for (int i = 0; i < a.length(); ++i)
		cout << a2[i] << " ";
	a2 = a3; // a2 是空的
	for (int i = 0; i < a2.length(); ++i) // a2.length() 返回 0
		cout << a2[i] << " ";
	cout << endl;
	a[3] = 100;
	CArray a4(a);
	for (int i = 0; i < a4.length(); ++i)
		cout << a4[i] << " ";
	return 0;
}

五、流插入运算符和流提取运算符的重载

  在c++编写程序时,我们如果要输入/输出一些东西,一般需要用到(cin)>>和(cout)<<运算符,称为流插入运算符(>>)与流提取运算符(<<)。

  首先我们来看流提取运算符,cout是在iostream中定义的,ostream类的对象。<<能作用在cout上是因为在iostream中对<<进行了重载,考虑了一些string、char、int、double等c++基本类的输出。但是如果我们要输出一个自定义类的对象,则我们需要另写一个重载函数,可以形象理解为cout.operator<<(自定义类对象)。

  考虑前面提到的复数类,如果我们需要输出一个复数类对象为a+bi的形式,则可以如下重载:

  由于需要访问类中的私有成员real和img,我们需要把该重载函数定义为友元函数。

  考虑创建一个a=1+2i,执行以下代码:

                        

  我们再来看流插入运算符,cin是在iostream中定义的,istream类的对象。>>能作用在cin上是因为在iostream中对>>进行了重载,考虑了一些string、char、int、double等c++基本类的输入。但是如果我们要输入一个自定义类的对象,则我们需要另写一个重载函数,可以形象理解为cin.operator>>(自定义类对象)。

  考虑复数类,如果我们需要输入一个复数类对象,输入两个double类的数a,b,创建一个a+bi的复数,则可以如下重载:

  由于需要访问类中的私有成员real和img,我们需要把该重载函数定义为友元函数。

  考虑输入2 3,输出结果如下:

                                      

六、类型转换运算符和自增、自减运算符的重载

Ⅰ.有一类特殊的运算符:类型转换运算符,如double(),int(),char()等,通过这些运算符可以对数据类型进行强制转化,同样的,我们也可以对自定义的类对象进行强行的类型转换,但这需要我们对相应的类型转换运算符进行重载。

   例如,我们对Complex类进行强制double类型转化,我们规定为返回Complex的实部real,则可以进行如下重载:

Ⅱ.c++中还存在一种特殊的运算符,即自增自减运算符,自增自减各有两种形式,有前置/后置之分(如++i为先加后取,i++为先取后加),为了区分所重载的是迁至运算符还是后置运算符,c++有如下规定:前置运算符作为一元运算符重载;后置运算符作为二元运算符重载,需要多写一个没用的参数。

                               

  我们仍然以Complex复数类为例

                  

                 

Ⅲ.我们还可以对指针操作“->”进行重载操作

运算符重载的注意事项:

(1)C++ 不允许定义新的运算符;

(2)重载后运算符的含义应该符合日常习惯;

(3)运算符重载不改变运算符的优先级;

(4)以下运算符不能被重载:".", ".*", "::", "?:", sizeof

(5)重载运算符(), [], ->或者赋值运算符=时,运算符重载函数必须声明为类的成员函数。

(6)重载运算符是为了让它能作用于对象,因此重载运算符,不允许操作数都不是对象 (有一个操作数是枚举类型也可以)。

  • 18
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值