天天看點

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—函數裡,傳回了引用是要出問題的。當然生命期長的,如在堆上配置設定的,調用函數傳過來的,傳回它們的引用自然沒問題。