拷貝構造函數和賦值運算符重載就是C++類默認六個函數之二。
拷貝構造函數:
如果?個構造函數的第?個參數是自身類類型的引用,且任何額外的參數都有默認值,則此構造函數 也叫做拷貝構造函數,也就是說拷貝構造是?個特殊的構造函數。
拷貝構造函數的特點:
- 拷貝構造函數是構造函數的?個重載。
- 拷貝構造函數的第?個參數必須是類類型對象的引?,使?傳值?式編譯器直接報錯,因為語法邏輯上會引發?窮遞歸調?。 拷貝構造函數也可以多個參數,但是第?個參數必須是類類型對象的引用,后?的參數必須有缺省值。
- C++規定自定義類型對象進?拷貝?為必須調?拷貝構造,所以這?自定義類型傳值傳參和傳值返回都會調?拷貝構造完成。
- 若未顯式定義拷貝構造,編譯器會?成?動?成拷貝構造函數。?動?成的拷貝構造對內置類型成 員變量會完成值拷貝/淺拷貝(?個字節?個字節的拷?),對?定義類型成員變量會調?他的拷貝構造。
- 傳值返回會產??個臨時對象調?拷貝構造,傳引?返回,返回的是返回對象的別名(引?),沒有產?拷貝。但是如果返回對象是?個當前函數局部域的局部對象,函數結束就銷毀了,那么使?引?返回是有問題的,這時的引?相當于?個野引?,類似?個野指針?樣。傳引?返回可以減少拷貝,但是?定要確保返回對象,在當前函數結束后還在,才能?引?返回。
?這是正確的拷貝構造函數,如果參數不是引用的話,首先傳參的時候就要進行拷貝,會調用拷貝構造,拷貝構造有會調用拷貝構造,就這樣無窮遞歸下去了,所以編譯器會直接報錯。
深拷貝與淺拷貝?
?淺拷貝就是一個字節一個字節的拷貝,一般情況下也沒有什么問題,但是當有動態開辟的內存時,如果使用淺拷貝的方式寫拷貝構造,那么當拷貝構造了對象時就會導致內存重復釋放的問題。
上面的類中就是淺拷貝,會導致內存重復釋放,
兩個對象,aa,bb中的a指向的都是同一塊堆內存,析構的時候,bb先析構,把這塊內存釋放了,aa析構的時候又釋放了一次,這就會報錯。
解決辦法就是深拷貝:
?就只要把a也進行動態開辟內存就行了。
就和前面的析構函數,只要類的成員中有動態開辟的成員,就要自己寫析構,寫拷貝構造并且是深拷貝。 沒有動態開辟的成員,就可以不寫析構函數和拷貝構造函數,默認提供的就可以了。
賦值運算符重載:
運算符重載:
- 當運算符被?于類類型的對象時,C++語?允許我們通過運算符重載的形式指定新的含義。C++規定類類型對象使用運算符時,必須轉換成調?對應運算符重載,若沒有對應的運算符重載,則會編譯報錯。
- 運算符重載是具有特殊名字的函數,他的名字是由operator和后?要定義的運算符共同構成。和其他函數?樣,它也具有其返回類型和參數列表以及函數體。
- 重載運算符函數的參數個數和該運算符作?的運算對象數量?樣多。?元運算符有?個參數,?元運算符有兩個參數,?元運算符的左側運算對象傳給第?個參數,右側運算對象傳給第?個參數。
- 如果?個重載運算符函數是成員函數,則它的第?個運算對象默認傳給隱式的this指針,因此運算符重載作為成員函數時,參數比運算對象少?個。
- 運算符重載以后,其優先級和結合性與對應的內置類型運算符保持?致。
- 不能通過連接語法中沒有的符號來創建新的運算符,比如:operator@.
- 有五個運算符是不能重載的:.* ,? ::? ?,sizeof ,? ?:?, .
- 重載操作符?少有?個類類型參數,不能通過運算符重載改變內置類型對象的含義,如: int operator+(int x, int y)
- ?個類需要重載哪些運算符,是看哪些運算符重載后有意義,?如Date類重載operator-就有意 義,但是重載operator+就沒有意義。
- 重載++運算符時,有前置++和后置++,運算符重載函數名都是operator++,?法很好的區分。 C++規定,后置++重載時,增加?個int形參,跟前置++構成函數重載,方便區分。
- 重載 << 和 >> 時,需要重載為全局函數,因為重載為成員函數,this指針默認搶占了第?個形參位置,第?個形參位置是左側運算對象,調?時就變成了 對象 << cout ,不符合使用習慣和可讀性。重載為全局函數把ostream/istream放到第?個形參位置就可以了,第?個形參位置當類類型對象。
下面以Date類為例,展示運算符重載
?頭文件
#pragma once
#include<iostream>
using namespace std;class Date
{
public://友元函數friend ostream& operator<<(ostream& cout, const Date& d);// 獲取某年某月的天數int GetMonthDay(int year, int month){static int d[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };int day = d[month];if (month == 2 && (year % 4 == 0 && year % 100 != 0 || year % 400 == 0))day++;return day;}// 全缺省的構造函數Date(int year = 1900, int month = 1, int day = 1);// 拷貝構造函數// d2(d1)Date(const Date& d);// 賦值運算符重載// d2 = d3 -> d2.operator=(&d2, d3)Date& operator=(const Date& d);// 析構函數//~Date();// 日期+=天數Date& operator+=(int day);// 日期+天數Date operator+(int day);// 日期-天數Date operator-(int day);// 日期-=天數Date& operator-=(int day);// 前置++Date& operator++();// 后置++Date operator++(int);// 后置--Date operator--(int);// 前置--Date& operator--();// >運算符重載bool operator>(const Date& d);// ==運算符重載bool operator==(const Date& d);// >=運算符重載bool operator >= (const Date& d);// <運算符重載bool operator < (const Date& d);// <=運算符重載bool operator <= (const Date& d);// !=運算符重載bool operator != (const Date& d);// 日期-日期 返回天數int operator-(const Date& d);private:int _year;int _month;int _day;
};
源文件?
#include"test.h"ostream& operator<<(ostream& cout, const Date& d)
{cout << d._year << "/" << d._month << "/" << d._day << endl;return cout;
}//全缺省構造函數
Date::Date(int year, int month, int day)//聲明和定義只能一個寫缺省值
{_year = year;_month = month;_day = day;
}// 拷貝構造函數
Date::Date(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}// 賦值運算符重載
Date& Date::operator=(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;return *this;
}// 日期+=天數
Date& Date::operator+=(int day)
{if (day < 0)return *this -= -day;_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_year++;_month = 1;}}return *this;
}// 日期+天數
Date Date::operator+(int day)
{Date tmp = *this;tmp += day; // 復用 += 運算符return tmp;
}// 日期-天數
Date Date::operator-(int day)
{Date tmp = *this;tmp -= day; // 復用 -= 運算符return tmp;
}// 日期-=天數
Date& Date::operator-=(int day)
{if (day < 0)return *this += -day;_day -= day;while (_day <= 0){_month--;if (_month == 0){_year--;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}// 前置++
Date& Date::operator++()
{*this += 1; // 復用+=return *this;
}// 后置++
Date Date::operator++(int)
{Date tmp = *this;*this += 1;//復用+=return tmp;
}// 后置--
Date Date::operator--(int)
{Date tmp = *this;*this -= 1;//復用-=return tmp;
}// 前置--
Date& Date::operator--()
{*this -= 1;//復用-=return *this;
}//大量的復用
// >運算符重載
bool Date::operator>(const Date& d)
{if (_year != d._year)return _year > d._year;if (_month != d._month)return _month > d._month;return _day > d._day;
}// ==運算符重載
bool Date::operator==(const Date& d)
{return (_year == d._year&& _month == d._month&& _day == d._day);
}// >=運算符重載
bool Date::operator >= (const Date& d)
{return (*this > d) || (*this == d);
}// <運算符重載
bool Date::operator < (const Date& d)
{return !(*this >= d);
}// <=運算符重載
bool Date::operator <= (const Date& d)
{return !(*this > d);
}// !=運算符重載
bool Date::operator != (const Date& d)
{return !(*this == d);
}// 日期-日期 返回天數
int Date::operator-(const Date& d)
{Date maxd = *this;Date mind = d;//防止改變原數據int n = 0;int flag = 1;if (*this < d){maxd = d;mind = *this;flag = -1;}while (mind != maxd){mind += 1;n++;}return n * flag;
}
補充:
Date d2(2025, 3, 15);Date d3 = d2;
上面的d3雖然用的是 = ,但是,實際上是調用拷貝構造函數,不是賦值重載!?只有原本就已經實例化了的對象才能用賦值重載,沒有實例化的就是構造函數。