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;
}