指派運算符重載詳解
為什麼要引入指派運算符重載這個概念
- 針對上面的這種情況,調用函數來進行比較,肯定是沒有直接用運算符号進行比較來的直接,是以說,引入運算符重載可以提高代碼的可讀性,使用起來也會更加的友善一些
- 使用者可以讓編譯器按照指定的規則對自定義類型對象直接進行一些運算符的操作
- 對>運算符的重載
指派運算符重載的概念
- C++為了增強代碼的可讀性引入了運算符重載,運算符重載是具有特殊函數名的函數,也具有其傳回值類型,函數名字以及參數清單,其傳回值類型與參數清單與普通的函數類似
- 函數名字為:關鍵字operator後面接需要重載的運算符符号。
- 函數原型:傳回值類型 operator操作符(參數清單)
下面通過代碼進行展示
#include<iostream>
#include<stdlib.h>
#include<string.h>
using namespace std;
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
cout << "Date(int,int,int):" << this << endl;
}
// Date(const Date& d)
// {
// _year = d._year;
// _month = d._month;
// _day = d._day;
// cout << "Date(Date&):" << this << endl;
// }
~Date()
{
cout << "~Date():" << this << endl;
}
private:
int _year;
int _month;
int _day;
};
void TestDate()
{
Date d1(2020, 5, 5);
Date d2(d1);
Date d3(2019, 5, 5); //本來希望d3的内容給成和d1内容是一樣的,但是由于輸入的時候輸入錯了
//是以我們利用指派,把d1中的内容指派給d3
//通過調試看出代碼沒有任何的問題
d3 = d1;
}
int main()
{
TestDate();
return 0;
}
#include<iostream>
#include<stdlib.h>
#include<string.h>
using namespace std;
class String
{
public:
String(const char* str = "")
{
cout << "String(const char* ):" << this << endl;
if (nullptr == str)
str = "";
_str = (char*)malloc(strlen(str) + 1);
strcpy(_str, str);
}
// 拷貝構造函數必須顯式提供
~String()
{
cout << "~String():" << this << endl;
free(_str);
_str = nullptr;
}
private:
char* _str;
};
void TestString()
{
String s1("hello");
String s2;
s2 = s1; //利用指派的操作
//通過監視可以看出其實s1和s2還是公用的同一塊記憶體空間,是以在資源釋放的時候還是會
//出現淺拷貝的問題
//而且這樣子直接指派的話,還會導緻s2的空間沒有釋放
//因為是直接把s1的位址複制到s2上面去的,是以造成了記憶體洩露
//是以對于string類的指派還是需要我們自己去完成
}
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
cout << "Date(int,int,int):" << this << endl;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
cout << "Date(Date&):" << this << endl;
}
// d1 = d2 = d3;
Date& operator=(const Date& d)
{
if (this != &d) //意思就是如果是自己給自己指派的話,就不需要來做了
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
// *this + day
// 問題:加完之後的結果可能是非法日期???
Date operator+(int day)
{
Date temp(*this);
temp._day += day;
return temp;
}
//先給一個臨時變量temp,然後再去修改臨時變量
//temp中内容,然後傳回temp,要知道,現在不是以引用的方式傳回的
//是以值得方式傳回的
// 首先傳回引用
// 傳回引用是否可行---->引用作為函數的傳回值類型:不能傳回函數棧上的空間
Date& DateAdd(int day)
{
_day += day;
return *this;
}
bool operator==(const Date& d)
{
return _year == d._year &&
_month == d._month &&
_day == d._day;
}
bool operator!=(const Date& d)
{
return !(*this == d);
}
// 前置++
// d2 = ++d1
Date& operator++()
{
_day += 1;
return *this;
}
// 後置++
// d2 = d1++
Date operator++(int)
{
Date temp(*this);
_day += 1;
return temp;
}
Date& operator--()
{
_day -= 1;
return *this;
}
Date operator--(int)
{
Date temp(*this);
_day -= 1;
return temp;
}
~Date()
{
cout << "~Date():" << this << endl;
}
private:
int _year;
int _month;
int _day;
int* _p;
};
// 不能重載非法運算符----
// Date operator@(int a)
// {
// Date d;
// return d;
// }
// int operator+(int left, int right)
// {
// return left + right;
// }
void TestDate()
{
int a = 10;
int b = 20;
int c;
c = a + b;
a = b = c;
Date d1(2019, 3, 22);
d1.DateAdd(3);
d1 = d1 + 3;
Date d2(d1);
d2 = d1++;
d2 = ++d1;
Date d3(2018, 3, 22);
d3 = d3;
d3 = d1;
d3.operator=(d1);
d1 = d2 = d3;
d1.operator=(d2.operator=(d3));
if (d3 == d1)
;
Date& d4 = d3;
d4 = d3;
}
int main()
{
TestDate();
return 0;
}
- 整形的參數傳什麼都可以,因為其實沒有什麼實際上的意義
指派運算符的重載
- 如果使用者沒有顯示給出指派運算符的重載,那麼編譯器會預設的去生成一份指派運算符的重載函數,但是具有淺拷貝的問題存在
- 上面寫的代碼,實際上是存在有兩個問題的
- 對指派運算符進行重載的操作
上面的代碼需要注意幾個問題
- 針對連續指派到底是誰給誰指派的問題
- a=b=c,得出的結論就是c指派給b,b指派給a
- 是以得出結論,指派運算符重載一定要有傳回值,不然是不支援連續指派的
- 還有一個問題就是,到底是傳回*this,還是傳回d,結論是要傳回this,這時根據連續指派的規則的出來的結論
- 要檢測一下,因為自己給自己指派其實是沒有任何意義的,是以在指派之前需要先檢測一下
注意
- 不能通過連接配接其他符号來建立新的操作符:比如operator@
- 重載操作符必須有一個類類型或者枚舉類型的操作數
- 用于内置類型的操作符,其含義不能改變,例如:内置的整型+,不能改變其含義
- 作為類成員的重載函數時,其形參看起來比操作數數目少1成員函數的操作符有一個認的形參this,限定為第一個形參
- .* 、:: 、sizeof 、?: 、. 注意以上5個運算符不能重載。這個經常在筆試選擇題中出現
- 不能去重載一些不能被重載的運算符
- 必須有一個類類型的
指派運算符主要有四點
- 參數類型
- 傳回值
- 檢測是否自己給自己指派
- 傳回*this
- 一個類如果沒有顯式定義指派運算符重載,編譯器也會生成一個,完成對象按位元組序的值拷貝。(按位元組拷貝就是原封不動的拷貝過去,其實就是淺拷貝)
可重載的運算符和不可重載的運算符
類類型對象的使用場景