Day6 : 运算符重载

Day6 : 运算符重载,第1张

Day6 : 运算符重载 一、定义

赋予运算符具有 *** 作自定义类型数据功能

二、实质

运算符重载的实质本身就是函数调用

三、重载函数的写法

    函数返回值   函数名(函数参数)
    函数返回值 :运算完成后的值决定的                Complex
    函数名    : operator 加上重载运算符组成函数名  operator+ 
    参数      :看运算符的 *** 作数,具体参数个数是要看你重载函数形式是什么
    函数体    : 写运算符具体想要的 *** 作

四、友元重载函数(方便在main函数中直接用)

以下实现自定义 复数相加的运算符。

#include
using namespace std;
class complex
{
public:
	complex(int a=0,int b=0):a(a),b(b){}
	friend complex operator+(complex a, complex b);
	void print()
	{
		cout << a << "t" << b;
	}
protected:
	int a;
	int b;
};
complex operator+(complex one, complex two)
{
	return complex(one.a + two.a, one.b + two.b);
}


int main()
{
	complex one(1, 2);
	complex two(2, 3);
	//隐式调用
	complex three;
	three = one + two;
	three.print();
	//显示调用
	complex four;
	four =operator+(one,two);
	four.print();
}
五、类成员函数重载

参数个数等于 *** 作减一(对象可以表示一个数据,所以参数应该少一个)

以比较复数大小的>为例

(返回值类型可以自定义)

class complex
{
public:
	complex(int a=0,int b=0):a(a),b(b){}
	bool operator>(complex one)
	{
		if (this->a > one.a)
			return true;
		else if (this->a == one.a && this->b > one.b)
			return true;
		else
			return false;
	}
	void print()
	{
		cout << a << "t" << b;
	}
protected:
	int a;
	int b;
};
int main()
{
	complex one(4, 2);
	complex two(2, 3);
	if (one > two)
	{
		cout << "one更大" << endl;
	}
	else
		cout << "233" << endl;
}
六、特殊运算符重载 ①流运算符重载:(string类型为何作为一个对象,可直接输出->流重载)

cin类型:istream类  

cout类型:ostream类(std自带)

流运算符:<<  >>  

第一个版本的写法:(缺点:无法连续进行cin和cout,保持流的状态)

#include

using namespace std;

class math
{
public:
	math(int score=1,int rank=2):score(score),rank(rank) {}
	void print_data()
	{
		cout << score << 't' << rank << endl;
	}
	friend void operator>>(istream& in,math&wbm);
	friend void operator<<(ostream& out, math& wbm);
protected:
	int score;
	int rank;
};
void operator>>(istream& in, math&wbm)
{
	in>>wbm.score>>wbm.rank;
}
void operator<<(ostream& out, math& wbm)
{
	out<>wbm;
	cout << wbm;
	return 0;
}

修改:返回一个istream类或者ostream类的对象即可。

#include

using namespace std;

class math
{
public:
	math(int score=1,int rank=2):score(score),rank(rank) {}
	void print_data()
	{
		cout << score << 't' << rank << endl;
	}
	friend istream& operator>>(istream& in,math&wbm);
	friend ostream& operator<<(ostream& out, math& wbm);
protected:
	int score;
	int rank;
};
istream& operator>>(istream& in, math&wbm)
{
	in>>wbm.score>>wbm.rank;
	return in;
}
ostream& operator<<(ostream& out, math& wbm)
{
	out<>wbm>>num;
	cout << wbm< 

至此,我们综合写一个complex复数的加减。

#include

using namespace std;

class complex
{
public:
	complex(int a = 1,int b = 2):a(a),b(b){}
	friend ostream& operator<<(ostream& out, complex& wbm);
protected:
	int a;//石不
	int b;//虚部
};
ostream& operator<<(ostream& out, complex& wbm)
{
	out << '(' << wbm.a << '+' << wbm.b << 'i' << ')' ;
	return out;
}
int main()
{
	complex wbm;
	cout << wbm;
	return 0;
}

        对运算符“<<”重载后,在程序中用“<<”不仅能输出标准类型数据,而且可以输出用户自己定义的类对象。用“cout<
        下面对怎样实现运算符重载作一些说明。程序中重载了运算符“<<”,运算符重载函数中的形参output是ostream类对象的引用,形参名output是用户任意起的。分析main函数最后第二行:
  cout< 运算符“<<”的左面是cout,前面已提到cout是ostream类对象。“<<”的右面是c3,它是Complex类对象。由于已将运算符“<<”的重载函数声明为Complex类的友元函数,编译系统把“cout<     operator<<(cout, c3)
即以cout和c3作为实参,调用下面的operator<<函数:
    ostream& operator<<(ostream& output,Complex& c)
    {
       output<<"("<        return output;
    }
调用函数时,形参output成为cout的引用,形参c成为c3的引用。因此调用函数的过程相当于执行:cout<<″(″< 请注意,上一行中的“<<”是C++预定义的流插入符,因为它右侧的操作数是字符串常量和double类型数据。执行cout语句输出复数形式的信息。然后执行return语句。
思考,return  output的作用是什么?回答是能连续向输出流插入信息。output是ostream类的对象,它是实参cout的引用,也就是cout通过传送地址给output,使它们二者共享同一段存储单元,或者说output是cout的别名。因此,return output就是return cout,将输出流cout的现状返回,即保留输出流的现状。
现在可以理解了为什么C++规定运算符“<<”重载函数的第一个参数和函数的类型都必须是ostream类型的引用,就是为了返回cout的当前值以便连续输出。

请读者注意区分什么情况下的“<<”是标准类型数据的流插入符,什么情况下的“<<”是重载的流插入符。如
    cout<<c3<<5< 有下划线的是调用重载的流插入符,后面两个“<<”不是重载的流插入符,因为它的右侧不是Complex类对象而是标准类型的数据,是用预定义的流插入符处理的。

②自增自减++和--运算符的重载

关键在于解决“前置和后置的问题”->引入一个int无用参数,表示后增

注意:通常return 一个匿名对象,注意若用引用,必须类型一致,即需要写const 或者写成右值引用的形式

#include

using namespace std;

class wbm
{
public:
	wbm(int age = 18,int rank=1):age(age),rank(rank){}
	wbm operator++(int)//后增重载
	{
		int num=age;
		age++;
		return wbm(num,rank);//匿名函数,参数类型为const 若为函数引用,需要改函数类型为const常量
		//以上代码等同于一行 return wbm(age++,rank);
	}
	wbm operator++()//不带int(无用参数)则为前置加加
	{
		return wbm(++age, rank);
	}
	void printdata()
	{
		cout << age << 't' << rank << endl;
	}
protected:
	int age;
	int rank;
};
int main()
{
	wbm bb;//默认18 1
	wbm temp;

	temp = bb++; //temp age=18   bb age=19
	temp.printdata();
	bb.printdata();

	temp = ++bb;//temp 20 bb 20
	temp.printdata();
	bb.printdata();

	return 0;
}
 ③文本重载:

zb小技巧:

#include

this_thread::sleep_for(3s);

此处的3s就是文本重载

自写:

 

打印出7200s

 七、类的对象的隐式转换
#include
using namespace std;

class boy
{
public:
	boy(int age=1):age(age){ }
	operator int()
	{
		return age;
	}
protected:
	int age;
};
int main()
{
	boy wbm;
	int wbmAge = wbm;
	cout << wbmAge << endl;
	return 0;
}
八、重载的综合案例 ①手写一个数组vector(待定。。。)

②封装一个新的int类型

#include
#include
using namespace std;

class Int
{
public:
	Int(int num):num(num){}
	string tostr()
	{
		return to_string(num);
	}
	Int operator+(const Int& one)//拜托一定注意是const 记得取引用& 为了防止拷贝本的产生
	{
		return Int(this->num + one.num);
	}
	//由于当用到-号的时候不知道是this->num在前还是在后,所以,我们这里采用友元的写法确认前后关系
	friend Int operator-(const Int& one,const Int& two)
	{
		return Int(one.num + two.num);
	}
	Int operator+=(const Int& a)
	{
		return Int(this->num + a.num);
	}
	Int operator++(int)//后置
	{
		return Int(this->num++);
	}
	Int operator++()
	{
		return Int(++(this->num));
	}
	Int operator&(const Int& a)
	{
		return Int(this->num & a.num);
	}
	bool operator!()
	{
		return !(this->num);
	}
	Int operator-()
	{
		return Int(-this->num);
	}
	friend istream& operator>>(istream& in,Int& a)//流重载要写友元
	{
		in >> a.num;
		return in;
	}
	friend ostream& operator<<(ostream& out, Int& b)
	{
		out << b.num;
		return out;
	}
	int* operator&()
	{
		return (&(this->num));
	}
	bool operator<(const Int& a)
	{
		return (this->num < a.num);
	}
protected:
	int num;
};
int main()
{
	Int a(1);
	Int b(2);
	Int sum = a + b;
	Int fub = -a;
	int* p = &a;
	cout << p << endl;
	cout << (a < b) << endl;
	cout << a << endl;
	cout << fub << endl;
	cout << sum << endl;
	return 0;
}

③重载[ ] 取值

#include

using namespace std;
//重载[]取值!
class vector
{
public:
	vector(int size):size(size)
	{
		base = new int[size] {0};
	}
	int& operator[](int num)//引用
	{
		return base[num];
	}
protected:
	int* base;
	int size;
};
int main()
{
	vector wbm(3);
	for (int i = 0; i < 3; i++)
	{
		cin >> wbm[i];//注意这里cin的细节,cin要对一个变量进行输入,所以上面在对[]进行重载的时候必须返回的int& 是一个变量
	}
	for (int i = 0; i < 3; i++)
	{
		cout << wbm[i];
	}
	return 0;
}

④重载()运算符  函数指针

#include

using namespace std;

class Function
{
	typedef void (*PF)();
public:
	Function(PF pf):pf(pf){}
	void operator()()
	{
		pf();
	}
protected:
	PF pf;
};
void print()
{
	cout << 12222 << endl;
}
int main()
{
	Function pf(print);
	pf();//通过一个类的对象来调用一个函数
	return 0;
}

补充一个更加直观的例子:

#include

using namespace std;

class Function
{
public:
	void operator()()
	{
		cout << "重载()" << endl;
	}
	//仿函数:让对象模仿函数的行为
	bool operator()(int a,int b)
	{
		return (a > b);
	}
	//以上两个函数就是()运算符的重载
protected:
}

⑤重载—>运算符: 智能指针

#include

using namespace std;

class wbm
{
public:
	wbm(string name,int age):name(name),age(age){}
	int age;
	string name;
};
class auto_pstr
{
public:
	auto_pstr(int * str):str(str){}
	auto_pstr(wbm* str):wbmstr(str){}
	int& operator*()//取引用,为了提供一个接口
	{
		return *str;
	}
	wbm* operator->()
	{
		return wbmstr;//重载->
	}
	~auto_pstr()//智能指针的原因:自动释放
	{
		if (wbmstr)
		{
			delete wbmstr;
			wbmstr = nullptr;
		}
		if (str)
		{
			delete str;
			str = nullptr;
		}
	}
protected:
	int* str;
	wbm* wbmstr;
};

int main()
{
	auto_pstr pstr(new int(19));
	cout << *pstr << endl;//*上面已经对*做过了重载
	auto_pstr wbmstr(new wbm("jie", 19));
	cout << wbmstr->name<做过了重载
	cout << wbmstr->age << endl;
	return 0;
}

注意:流重载 只可以用友元进行重载

            = () ->  [ ] 仅可以采用类成员重载(->还没有参数(类成员函数的重载参数-1))

          .    .*   ?:   ::(作用域分辨符)不能被重载

课后作业:重载实战: 封装一个Array类,实现定长数组的 *** 作

#include

using namespace std;
class Array
{
public:
	Array(int cursize=0):cursize(cursize)
	{
		base = new int[cursize]{0};//初始化为0
	}
	int& size()
	{
		return cursize;
	}
	//对[]进行重载
	int& operator[](int num)
	{
		return base[num];
	}
	//流重载
	friend istream& operator>>(istream& in, Array a)
	{
		for (int i = 0; i < a.cursize; i++)
		{
			in >> a.base[i];
		}
		return in;
	}
	friend ostream& operator<<(ostream& out, Array b)
	{
		for (int i = 0; i < b.cursize; i++)
		{
			out << b.base[i];
			out << " ";
		}
		return out;
	}
	//+重载
	Array operator+(Array b)
	{
		Array sum(this->cursize + b.cursize);
		for (int i = 0; i < this->cursize; i++)
		{
			sum.base[i] = this->base[i];
		}
		int i = 0;
		for (int j= this->cursize; j < this->cursize + b.cursize; j++)
		{
			sum.base[j] = b.base[i++];
		}
		return sum;
	}
protected:
	int* base;
	int cursize;
};

int main()
{
	//以下测试代码要能够成功运行
	Array array(4);
	for (int i = 0; i < array.size(); i++)
	{
		cin >> array[i];                        //说明对[]进行了重载
	}
	for (int i = 0; i < array.size(); i++)
	{
		cout << array[i];
	}
	//实现数组的连接
	Array one(3);   //输入1 2 3 
	cin >> one;								       //流重载
	Array two(3);   //输入2 3 4
	cin >> two;
	Array  sum = one + two;                        //加号重载
	cout << sum<< endl;  //打印1 2 3 2 3 4
	Array num;
	num = sum;
	cout << num << endl;
	return 0;
}

以上代码有点问题:写了指针,没有释放,内存泄漏。写了之后发现爆了

->原因:临时变量sum二次释放!(在重载+的函数内)也就是在sum(局部)转交权给了函数后,这个函数的返回值实际上是一个指向相同地址的Array对象,但指向的地址里面的内容已经被清空,所以当再次析构会爆

一次改进:在这个sum(局部)new一个新的对象(指向一个新的地址)(占用堆区内存)

但无法释放,虽然可以运行不爆,但内存泄露了。

	Array operator+(Array& b)//&防止拷贝本产生,提高效率
	{
		Array* sum=new Array(this->cursize + b.cursize);
		for (int i = 0; i < this->cursize; i++)
		{
			sum->base[i] = this->base[i];
		}
		int i = 0;
		for (int j= this->cursize; j < this->cursize + b.cursize; j++)
		{
			sum->base[j] = b.base[i++];
		}
		return *sum;
	}

所以并不太推荐这种写法。

 然鹅上面的代码依旧会爆(原因:用的默认拷贝构造(浅拷贝)这里,return的时候,实际上就是相当于一个拷贝的(隐式)过程,但指向的同一块区域,故析构必会出问题->重新写一个拷贝构造函数(深)包括下面的sum=one+two也会导致相类似的问题,所以也要对=号进行一个重载)

#include

using namespace std;
class Array
{
public:
	Array(int cursize=0):cursize(cursize)
	{
		base = new int[cursize]{0};//初始化为0
	}
	int& size()
	{
		return cursize;
	}
	//对[]进行重载
	int& operator[](int num)
	{
		return base[num];
	}
	//流重载
	friend istream& operator>>(istream& in, Array& a)
	{
		for (int i = 0; i < a.cursize; i++)
		{
			in >> a.base[i];
		}
		return in;
	}
	friend ostream& operator<<(ostream& out, Array& b)
	{
		for (int i = 0; i < b.cursize; i++)
		{
			out << b.base[i];
			out << " ";
		}
		return out;
	}
	//+重载
	Array& operator+(Array& b)//&防止拷贝本产生,提高效率
	{
		Array* sum=new Array(this->cursize + b.cursize);
		for (int i = 0; i < this->cursize; i++)
		{
			sum->base[i] = this->base[i];
		}
		int i = 0;
		for (int j= this->cursize; j < this->cursize + b.cursize; j++)
		{
			sum->base[j] = b.base[i++];
		}
		return *sum;
	}
	void operator=(Array& a)
	{
		this->cursize = a.cursize;
		this->base = new int[a.cursize];
		for (int i = 0; i < this->cursize; i++)
		{
			this->base[i] = a.base[i];
		}
	}
	Array(Array& a)
	{
		this->cursize = a.cursize;
		this->base = new int[a.cursize];
		for (int i = 0; i < this->cursize; i++)
		{
			this->base[i] = a.base[i];
		}
	}
	~Array()
	{
		delete[]base;
	}
protected:
	int* base;
	int cursize;
};

int main()
{
	//以下测试代码要能够成功运行
	Array array(4);
	for (int i = 0; i < array.size(); i++)
	{
		cin >> array[i];                        //说明对[]进行了重载
	}
	for (int i = 0; i < array.size(); i++)
	{
		cout << array[i];
	}
	//实现数组的连接
	Array one(3);   //输入1 2 3 
	cin >> one;	//流重载
	Array two(3);   //输入2 3 4
	cin >> two;
	Array  sum = one + two;                        //加号重载
	cout << sum<< endl;  //打印1 2 3 2 3 4
	Array num;
	num = sum;
	cout << num << endl;
	return 0;
}

此为调试最终版,但仍会有内存的泄露问题(建议用智能指针写。)

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5711457.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存