天天看點

C++核心程式設計---4.5 類和對象-運算符重載【P121~P126】4.5 運算符重載

C++核心程式設計---4.5 類和對象-運算符重載【P121~P126】

  • 4.5 運算符重載
    • 4.5.1 加号運算符重載
    • 4.5.2 左移運算符重載
    • 4.5.3 遞增運算符重載
    • 4.5.4 指派運算符重載
    • 4.5.5 關系運算符重載
    • 4.5.6 函數調用運算符重載

4.5 運算符重載

運算符重載概念:對已有的運算符重新定義,賦予其另一種功能,以适應不同的資料類型。

4.5.1 加号運算符重載

作用: 實作兩個自定義資料類型相加的運算。

對于内置的資料類型,編譯器知道該如何進行運算

int a = 10;
int b = 10;
int c = a + b;
           

那麼對于一個自定義的資料類型,編譯器該如何處理呢?

class Person
{
public:
	int m_A;
	int m_B;
};

Person p1;
p1.m_A = 10;
p1.m_B = 10;

Person p2;
p2.m_A = 10;
p2.m_B = 10;

Person p3 = p1 + p2;
           

通過我們自己書寫成員函數,實作兩個對象相加屬性後傳回新的對象

Person PersonAddPerson(Person &p)
{
	Person temp;
	temp.m_A = this->m_A + p.m_A;
	temp.m_B = this->m_B + p.m_B;
	return temp;
}
           

為了避免差異性,編譯器給起了一個通用的名稱 operator+

通過成員函數重載+号:

Person operator+(Person &p)
{
	Person temp;
	temp.m_A = this->m_A + p.m_A;
	temp.m_B = this->m_B + p.m_B;
	return temp;
}
Person p3 = p1.operator+(p2);
//簡化為
Person p3 = p1 + p2;
           

通過全局函數重載+号:

Person operator+(Person p1,Person p2)
{
	Person temp;
	temp.m_A = p1.m_A + p2.m_A;
	temp.m_B = p1.m_B + p2.m_B;
	return temp;
}
Person p3 = operator+(p1,p2)
//簡化為
Person p3 = p1 + p2;
           
#include<iostream>
#include<string>
using namespace std;

//加号運算符重載
class Person
{
public:
	1、成員函數重載+号
	//Person operator+(Person &p)
	//{
	//	Person temp;
	//	temp.m_A = this->m_A + p.m_A;
	//	temp.m_B = this->m_B + p.m_B;
	//	return temp;
	//}

	int m_A;
	int m_B;
};

//2、全局函數重載+号
Person operator+(Person &p1, Person &p2)
{
	Person temp;
	temp.m_A = p1.m_A + p2.m_A;
	temp.m_B = p1.m_B + p2.m_B;
	return temp;
}

//3、函數重載
Person operator+(Person &p1, int num)
{
	Person temp;
	temp.m_A = p1.m_A + num;
	temp.m_B = p1.m_B + num;
	return temp;
}

void test01()
{
	Person p1;
	p1.m_A = 10;
	p1.m_B = 10;

	Person p2;
	p2.m_A = 10;
	p2.m_B = 10;

	成員函數重載本質調用
	//Person p3 = p1.operator+(p2);

	//全局函數重載的本質調用
	Person p3 = operator+(p1, p2);

	//簡化形式
	//Person p3 = p1 + p2;

	//另外,加号運算符重載也可以發生函數重載
	Person p4 = operator+(p1, 30);
	//Person p4 = p1 + 10;

	cout << "p3.m_A =  " << p3.m_A << endl;
	cout << "p3.m_B =  " << p3.m_B << endl;
	cout << "p4.m_A =  " << p4.m_A << endl;
	cout << "p4.m_B =  " << p4.m_B << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}
           

總結:

  • 1、對于内置的資料類型的表達式的運算符是不可能改變的
  • 2、不要濫用運算符重載

4.5.2 左移運算符重載

作用: 可以輸出自定義的資料類型。

對于内置的資料類型,可以直接輸出

int a = 10;
cout << a << endl;
           

那麼對于自定義的資料類型該如何輸出呢?

Person p;
p.m_A = 10;
p.m_B = 10;

cout << p <<endl;
           

這樣顯然是行不通的,我們需要重載這個左移符号,讓它知道該怎麼輸出 p 裡邊的屬性。

#include<iostream>
#include<string>
using namespace std;

class Person
{
	friend ostream & operator<<(ostream &out, Person p);

public:
	Person(int a, int b)
	{
		m_A = a;
		m_B = b;
	}
private:
	//利用成員函數重載左移運算符	p.operator<<(cout)	簡化版本 p << cout
	//不會利用成員函數重載<<運算符,因為無法實作cout在左側

	//void operator<<(cout)
	//{
	//
	//}

	int m_A;
	int m_B;
};

//隻能利用全局函數重載左移運算符
//引用&out是cout 的别名,共同操作cout這個輸出流對象的記憶體
ostream & operator<<(ostream &out , Person p)		//簡化版本  cout << p
{
	out << "m_A = " << p.m_A << "    m_B = " << p.m_B;
	return out;
}

void test01()
{
	Person p(10,10);
	cout << p << endl ;
}


int main()
{
	test01();

	system("pause");
	return 0;
}
           

總結:重載左移運算符配合友元可以實作輸出自定義資料類型。

4.5.3 遞增運算符重載

作用: 通過重載遞增運算符,實作自己的整型資料。

内置的遞增運算符 ++

int a = 10;
cout << ++a <<endl;//11
cout << a << endl;//11

int b = 10;
cout << b++ <<endl;//10
cout << b << endl;//11
           

自定義一個整形資料

class MyInteger
{
public:
	MyInteger()
	{
		m_Num = 0;
	}
private:
	int m_Num;
};
           

想實作這樣的輸出:

MyInteger myint;
cout << myint << endl;//0;
cout << ++myint << endl;//1
cout << myint++ << endl;//1
cout << myint << endl;//2
           
#include<iostream>
#include<string>
using namespace std;

class MyInteger
{
	friend ostream & operator<<(ostream &out, MyInteger myint);
public:
	MyInteger()
	{
		m_Num = 0;
	}
	//1、重載前置++運算符	傳回引用是為了保證對一個資料進行遞增操作
	MyInteger& operator++()
	{
		//先進行++運算
		m_Num++;
		//再将自身做傳回
		return *this;
	}
	//2、重載後置++運算符
	MyInteger operator++(int)	//int  代表占位參數,可以用于區分前置和後置遞增,否則不能滿足函數重載條件
	{
		//先	記錄當時結果
		MyInteger temp = *this;
		//後	遞增
		m_Num++;
		//再	将記錄結果傳回
		return temp;
	}

private:
	int m_Num;
};

ostream & operator<<(ostream &out, MyInteger myint)
{
	out << myint.m_Num;
	return out;
}

void test01()
{
	MyInteger myint;
	cout << ++myint << endl;
}

void test02()
{
	MyInteger myint;
	cout << myint++ << endl;
	cout << myint << endl;
}

int main()
{
	test01();
	test02();

	system("pause");
	return 0;
}
           

注意:

  • 1、前置遞增傳回的是值
  • 2、後置遞增傳回的是引用

4.5.4 指派運算符重載

C++編譯器至少給一個類添加4個函數:

  • 1、預設構造函數(無參,函數體為空)
  • 2、預設析構函數(無參,函數體為空)
  • 3、預設拷貝構造函數,對屬性進行值拷貝
  • 4、指派運算符 operator=,對屬性進行值拷貝

如果類中有屬性指向堆區,做指派操作時也會出現深淺拷貝問題。

#include<iostream>
#include<string>
using namespace std;

class Person
{
public:

	Person(int age)
	{
		m_Age = new int(age);
	}

	~Person()
	{
		if (m_Age != NULL)
		{
			delete m_Age;
			m_Age = NULL;
		}
	}

	//重載 指派運算符
	Person & operator=(Person &p)
	{
		//編譯器是提供淺拷貝
		//m_Age = p.m_Age;

		//應該先判斷是否有屬性在堆區,如果有先釋放幹淨,然後再進行深拷貝
		if (m_Age != NULL)
		{
			delete m_Age;
			m_Age = NULL;
		}
		//深拷貝
		m_Age = new int(*p.m_Age);

		//傳回對象本體
		return *this;
	}

	int *m_Age;


};

void test01()
{
	Person p1(12);
	Person p2(20);
	Person p3(30);

	p3 = p2 = p1;

	cout << "p1的年齡是: " << *p1.m_Age << endl;
	cout << "p2的年齡是: " << *p2.m_Age << endl;
	cout << "p3的年齡是: " << *p3.m_Age << endl;
}


int main()
{
	test01();

	system("pause");
	return 0;
}
           

4.5.5 關系運算符重載

作用: 重載關系運算符,可以讓兩個自定義類型對象進行對比操作

#include<iostream>
#include<string>
using namespace std;

class Person 
{
public:
	Person(string name, int age)
	{
		m_Name = name;
		m_Age = age;
	
	}
	
	bool operator == (Person &p)
	{
		if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
		{
			return true;
		}
		return false;
	}
	
	string m_Name;
	int m_Age;
};

void test01()
{
	Person p1("Tom",18);
	Person p2("Jerry", 18);

	if (p1 == p2)
	{
		cout << "p1 和 p2 是相等的!" << endl;
	}
	else
	{
		cout << "p1 和 p2 是不相等的!" << endl;
	}
}

int main()
{
	test01();

	system("pause");
	return 0;
}
           

4.5.6 函數調用運算符重載

  • 函數調用運算符()也可以重載
  • 由于重載後使用的方式非常像函數的調用,是以稱為仿函數
  • 仿函數沒有固定寫法,非常靈活
#include<iostream>
#include<string>
using namespace std;

class MyPrint
{
public:
	void operator()(string test)
	{
		cout << test << endl;
	}
};

void test01()
{
	MyPrint myPrint;
	myPrint("Hello World !");//由于重載後使用的方式非常像函數的調用,是以稱為仿函數
}

class MyAdd
{
public:
	int operator()(int num1 , int num2)
	{
		return num1 + num2;
	}
};

void test02()
{
	MyAdd myadd;
	int ret = myadd(100, 100);
	cout << "ret = " << ret << endl;

	//匿名函數對象
	cout << MyAdd()(100, 100) << endl; 
}

int main()
{
	//test01();
	test02();

	system("pause");
	return 0;
}
           

繼續閱讀