天天看点

c++运算符重载练习

#include <iostream>
using namespace std;

class Test
{
public:
	Test(int a = 0, int b = 0)
	{
		this->a = a;
		this->b = b;
	}
public:
	int a;
	int b;
public:
	//全局函数
	Test T_add(Test &t1, Test &t2)
	{
		Test t3;
		t3.a = t1.a + t2.a;
		t3.b = t1.b + t2.b;
		return t3;
	}
	Test& add(Test &t2) //面向对象编程
	{
		a = a + t2.a;
		b = b + t2.b;
		return *this;
	}
};
//全局函数
Test T_add(Test &t1, Test &t2)
{
	Test t3;
	t3.a = t1.a + t2.a;
	t3.b = t1.b + t2.b;
	return t3;
}

Test T_add01(Test *pthis, Test &t1, Test &t2)
{
	Test t3;
	t3.a = t1.a + t2.a;
	t3.b = t1.b + t2.b;
	return t3;
}
//从成员函数转换为全局函数,只需要加一个this指针(一个指向本类的指针)
//从全局函数转换为类的成员函数,只需要减去一个左操作数
void Demo01()
{
	Test t1(2, 3), t2(3, 4);
	Test t3 = T_add(t1, t2);
	t3 = t1.T_add(t1, t2);
}
void main()
{
	system("pause");
}
           
c++运算符重载练习
c++运算符重载练习

从成员函数转换为全局函数,只需要加一个this指针(一个指向本类的指针),而从从全局函数转换为类的成员函数,只需要减去一个左操作数即可,正如上面的代码所示。

c++运算符重载练习
c++运算符重载练习

下面是关于友元函数的一个例子:

#include <iostream>
using namespace std;
//const c 莫安排活
//register cpu身边的小太监
//typedef 混号王
class Test
{
public:
	Test(int a = 0, int b = 0)
	{
		this->a = a;
		this->b = b;
	}
	int getA()
	{
		return a;
	}
	int getB()
	{
		return b;
	}
	//友元函数的特点是有一个参数肯定是友元类的指针或者引用
	friend int OpMen(Test *p, int a); //函数声明放在哪里都一样
private:
	int a;
	int b;
};

int OpMen(Test *p, int a) //Test类的好朋友,友元函数
{
	p->a = a;
	return 0;
}
void main()
{
	Test t1;
	OpMen(&t1, 5);
	system("pause");
}

           
c++运算符重载练习
c++运算符重载练习

友元函数并不是类的成员函数,它是一个全局函数,只需要在类里面声明一下就行了,放在public,private,protect任意一个域下都,没关系,使用友元函数唯一的好处是可以在函数里访问类的私有数据成员,就好像类打开了一扇窗一样,友元函数一般用的不多(C+++设计的败笔),可是有时候会用,了解一下即可。

c++运算符重载练习
c++运算符重载练习

运算符重载练习

#include <iostream>
using namespace std;

class Complex
{
public:
	int a;
	int b;
public:
	Complex(int a = 0, int b = 0)
	{
		this->a = a;
		this->b = b;
	}
	void print()
	{
		printf("%d+%di\n", a, b);
	}

};
Complex add(Complex &lop, Complex &rop)
{
	Complex result;
	result.a = lop.a + rop.a;
	result.b = lop.b + rop.b;
	return result;
}

Complex operator+(Complex &lop, Complex &rop)//重载
{
	Complex result;
	result.a = lop.a + rop.a;
	result.b = lop.b + rop.b;
	return result;
}
void Demo01()
{
	Complex c1(1, 2), c2(3, 4);
	//c1 = c1 + c2; //编译器压根不知道怎么加,但是编译器会给你提供一种机制,会让你实现自定义加操作
	Complex c3 = add(c1, c2); //直接调用函数add
}
void Demo02()
{
	Complex c1(1, 2), c2(3, 4);
	//操作符重载首先是通过函数实现的
	//+操作符有两个参数,左操作数和右操作数,特别留意左操作数
	Complex c3 = c1 + c2; //实现了重载
	//Complex c3 = operator+(c1, c2);//这样写也不会错
}

void main()
{
	Demo02();
	system("pause");
}

           
c++运算符重载练习
c++运算符重载练习

一般的类变量是设置成private属性的,因此我们一般都用友元函数实现运算符重载。

#include <iostream>
using namespace std;

class Complex
{
private:
	int a;
	int b;
public:
	Complex(int a = 0, int b = 0)
	{
		this->a = a;
		this->b = b;
	}
	//通过友元函数实现操作符重载
	friend Complex operator+(Complex &lop, Complex &rop);
	friend Complex &operator++(Complex &lop);
	friend Complex operator++(Complex &lop, int/*占位符*/);
	//通过类的成员函数实现减号操作
	Complex operator-(Complex &rop) //左操作数隐藏在this指针里面
	{
		Complex res;
		res.a = a - rop.a;
		res.a = b - rop.b;
		return res;
	}
	Complex &operator--()
	{
		a--;
		b--;
		return *this;
	}
	Complex operator--(int) //后置--的成员函数实现
	{
		Complex tmp = *this;
		this->a--;
		this->b--;
		return tmp;
	}
	void print()
	{
		printf("%d + %di\n", a, b);
	}

};

Complex operator++(Complex &lop, int/*占位符*/)
{
	Complex tmp = lop;
	lop.a++;
	lop.b++;
	return tmp;
}
//全局函数原型推导
Complex &operator++(Complex &lop)
{
	lop.a++;
	lop.b++;
	return lop;
}
Complex operator+(Complex &lop, Complex &rop)
{
	Complex result;
	result.a = lop.a + rop.a;
	result.b = lop.b + rop.b;
	return result;
}
void Demo01()
{
	Complex c1(1, 2), c2(3, 4);
	//c1 = c1 + c2; //编译器压根不知道怎么加,但是编译器会给你提供一种机制,会让你实现自定义加
	Complex c4 = c1 - c2;
	Complex c5 = c1.operator-(c2);
	//目标,通过类的成员函数完成操作符重载
	//1.要承认操作符操作是一个函数,要写函数原型
	//2.写出函数调用语言
	//3.完善函数原型

}
void Demo02()
{
	Complex c1(1, 2), c2(3, 4);
	//Complex c3 = operator+(c1, c2);
	//操作符重载首先是通过函数实现的
	//+操作符有两个参数,左操作数和右操作数,特别留意左操作数
	Complex c3 = c1 + c2;

}

void Demo03()
{
	Complex c1(1, 2), c2(3, 4);
	c1++;
	c1.print();
	--c2;
	c2++;
	//先使用c2的属性,然后让属性+++
	//operator++(c2, int/*占位符*/);//后置++
	c2.print();
	//成员函数实现后置--
}

void main()
{
	Demo03();
	system("pause");
}

           
c++运算符重载练习
c++运算符重载练习

使用友元函数和类的成员函数都可以实现操作符重载,正如同前面所提到的,成员函数由于已经包含了this指针,所以省略了一个操作数,其实两者差别不大。不过也存在差别,成员函数和友元函数重载操作符各有用处。规范的写法是,一般可以用成员函数实现的操作符重载就不用友元函数,所以使用友元函数重载的地方就很少了,一处场景如下下段代码所示。

c++运算符重载练习
c++运算符重载练习

关于前置++和后置++需要注意几点:

c++运算符重载练习
c++运算符重载练习

前置++的版本一般是这么写:foo&operator++()//前置++,前置返回引用

c++运算符重载练习
c++运算符重载练习

后置++一般这么写:foooperator++(int)//后置++,后置返回对象

c++运算符重载练习
c++运算符重载练习

前置返回引用,后置返回对象,这是通用做法。

class foo
{
public:
	foo operator++() //后置++,后置返回对象
	{
		a++;
		return *this;
	}
	foo& operator++(int) //前置++,前置返回引用,int占位符,只是标记,将前置和后置区分开来
	{
		a++;
		return *this;
	}
public:
	foo(int a = 0)
	{
		this->a = a;
	}
private:
	int a;
};
           
c++运算符重载练习
c++运算符重载练习

在实际中,我们一般不用友元函数重载运算符,一般都用成员函数,但是使用友元函数重载运算符也有用武之地,下面是规范的写法:

#include <iostream>
using namespace std;

class Complex
{
private:
	int a;
	int b;
public:
	Complex(int a = 0, int b = 0)
	{
		this->a = a;
		this->b = b;
	}
	//通过类的成员函数实现减号操作
	Complex operator-(Complex &rop) //左操作数隐藏在this指针里面
	{
		Complex res;
		res.a = a - rop.a;
		res.a = b - rop.b;
		return res;
	}
	Complex operator+(Complex &rop) //左操作数隐藏在this指针里面
	{
		Complex res;
		res.a = a + rop.a;
		res.a = b + rop.b;
		return res;
	}
	Complex &operator++()
	{
		a++;
		b++;
		return *this;
	}
	Complex operator++(int) //后置--的成员函数实现
	{
		Complex tmp = *this;
		this->a++;
		this->b++;
		return tmp;
	}
	Complex &operator--()
	{
		a--;
		b--;
		return *this;
	}
	Complex operator--(int) //后置--的成员函数实现
	{
		Complex tmp = *this;
		this->a--;
		this->b--;
		return tmp;
	}
	void print()
	{
		printf("%d + %di\n", a, b);
	}
	friend ostream& operator<<(ostream &out, Complex &op); //这里才是friend函数的真正用武之地
protected:
private:

};

ostream& operator<<(ostream &out, Complex &op)
{
	out << op.a << " " << op.b << endl;
	return out;
}
void Demo01()
{
	Complex c1(1, 2), c2(3, 4);
	int a = 10;
	char *p = "abc";
	cout << p << endl;
	//没有办法在cout类里面添加函数operator<<只能通过全局函数实现
	cout << c1;
	cout << c1 << endl << "链式编程测试" << endl;
	//若void operator<<(ostream &out, Complex &op);
	//operator<<(cout, &c1);
	//void << endl;
	//函数返回值当左值的时候,需要返回一个对象的引用

}

void main()
{
	Demo01();
	system("pause");
}
           
c++运算符重载练习
c++运算符重载练习

特别需要注意的地方是,如果ostream&operator<<(ostream&out,Complex&op)写成了voidoperator<<(ostream&out,Complex&op),虽然执行cout<<c1;不会出错,但是执行cout<<c1<<endl<<"链式编程测试"<<endl;会出错,这是因为operator=的返回值的缘故,cout<<c1相当于执行operator<<(cout,c1),返回void,接下来执行void <<endl,不错才怪。

c++运算符重载练习
c++运算符重载练习

同时,上面的ostream&operator<<(ostream&out,Complex&op)是友元函数运用的绝好时期,因为如果要用成员函数实现<<运算符重载,那么必须在cout里面实现,cout并没有在成员函数里实现operator<<(Complex&op),当然它也不可能实现,怎么办呢?恰好运用友元函数可以解决这个问题。

c++运算符重载练习
c++运算符重载练习

接下来是运算符重载的一个很好的例子:

/*Array.h*/
#ifndef _ARRAY_H_
#define _ARRAY_H_
#include <iostream>
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();
public:
	int& operator[](int index);
	Array& operator=(Array &rop);
	bool operator==(Array &rop);
	bool operator!=(Array &rop);
};


//[]  =  ==  !=
//


#endif
           
/*Array.cpp*/
#include "iostream"
#include "Array.h"
using namespace std;

//区别 如果不加引用会构造一个匿名对象(临时对象)
//开销会非常大
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;
}

//需要当左值,返回引用
//返回引用是要注意生命周期
/************************************************************************/
/*
Complex &operator-(operator &rop)
{
Complex tmp;
tmp.a = this.a - rop.a;
tmp.b = this.b - rop.b;
return tmp; //这样返回会出问题
return rop;//这样返回没问题
}
*/
/************************************************************************/
//printf("%d", a[i]);
//a[i] = 10;
int& Array::operator[](int index) //函数返回值当左值 a[i] = 10;
{
	if (index > mLength)
	{

	}
	return mSpace[index];
}

//测试用例
//a2 = a3;
//a1 = a2 = a3;连等操作 =从右向左 先执行a2 = a3返回 a2再执行 a1 = a2;
//a1.operator=(a2.operator=(a3)))
//函数返回值当左值
Array& Array::operator=(Array &rop)
{
	if (mSpace != NULL)
	{
		delete[]mSpace;
		mLength = 0;
		mSpace = NULL;
	}
	mSpace = new int[rop.mLength];
	mLength = rop.length();
	for (int i = 0; i < rop.mLength; ++i)
	{
		mSpace[i] = rop.getData(i);
	}
	return *this;
}
bool Array::operator==(Array &rop)
{//先判断长度是否相等,在判断数据是否相等
	if (rop.length() != mLength)
	{
		return false;
	}
	for (int i = 0; i < mLength; ++i)
	{
		if (mSpace[i] != rop.getData(i))
		{
			return false;
		}
	}
	return true;
}
bool Array::operator!=(Array &rop)
{
	return !this->operator==(rop);
}
           
c++运算符重载练习
c++运算符重载练习

要特别注意运算符重载的返回值,就像上面演示的代码一样,int&Array::operator[](intindex) 之所以返回int&,是因为它的性质所决定的,int a = array[5];[]的返回值可以做右边的操作数,这时候返回int是没问题的,不过array[5] = 10;这一句[]的返回值要做左值,就不能返回int了,因为返回的int无法修改,所以要返回int&。

c++运算符重载练习
c++运算符重载练习

同时也要注意返回类还是类的引用,返回类的时候,如

c++运算符重载练习
c++运算符重载练习

Complexoperator--(int)//后置--的成员函数实现

    {

       Complextmp=*this;

       this->a--;

       this->b--;

       return tmp;

    }

c++运算符重载练习
c++运算符重载练习

在主函数里假设这么调用Complex t2= t1--;

c++运算符重载练习
c++运算符重载练习

开销是比较大的,返回tmp时c++会创建一个临时对象,调用拷贝构造函数用tmp初始化该临时对象,此时C++做了优化,会将t2和该临时对象绑定起来。

c++运算符重载练习
c++运算符重载练习

若这么调用Complex t2; t2 = t1--;,这时候开支更大了,返回tmp时c++会创建一个临时对象,调用拷贝构造函数用tmp初始化该临时对象,将该临时对象赋给t2,调用t2的=操作,调用该操作之后,该临时对象会立马析构掉。

c++运算符重载练习
c++运算符重载练习

而返回引用时省略了临时变量,所以你要考量值不值。

c++运算符重载练习
c++运算符重载练习

并非所有时候都能返回引用,如如上面的代码,如果返回tmp的引用,这是肯定不行的,因为tmp的生命期只在operator—函数里,返回了引用是要出问题的。当然生命期长的,如在堆上分配的,调用函数传过来的,返回它们的引用自然没问题。