拷貝構(gòu)造函數(shù)
概念
在現(xiàn)實(shí)生活中,可能存在一個與你一樣的自己,我們稱其為雙胞胎
那在創(chuàng)建對象時,可否創(chuàng)建一個與已存在對象一某一樣的新對象呢?
拷貝構(gòu)造函數(shù):只有單個形參,該形參是對本類類型對象的引用(一般常用const修飾),在用已存在的類類型對象創(chuàng)建新對象時由編譯器自動調(diào)用
特征
拷貝構(gòu)造函數(shù)也是特殊的成員函數(shù),其特征如下:
- 拷貝構(gòu)造函數(shù)是構(gòu)造函數(shù)的一個重載形式
- 拷貝構(gòu)造函數(shù)的參數(shù)只有一個且必須是類類型對象的引用,使用傳值方式編譯器直接報(bào)錯,因?yàn)闀l(fā)無窮遞歸調(diào)用
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// Date(const Date& d) // 正確寫法
Date(const Date& d) // 錯誤寫法:編譯報(bào)錯,會引發(fā)無窮遞歸
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(d1);
return 0;
}
這里大家可能會很疑惑為什么會出現(xiàn)無窮遞歸的現(xiàn)象 下面我畫一個圖大家理解一下
- 若未顯式定義,編譯器會生成默認(rèn)的拷貝構(gòu)造函數(shù)。 默認(rèn)的拷貝構(gòu)造函數(shù)對象按內(nèi)存存儲按字節(jié)序完成拷貝,這種拷貝叫做淺拷貝,或者值拷貝
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& dd)
{
_year = dd._year;
_month = dd._month;
_day = dd._day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
void func1(Date& d)
{
d.Print();
}
int main()
{
//拷貝構(gòu)造函數(shù)
Date d1(2023, 10, 22);
func1(d1);
Date d2(d1);
注意:在編譯器生成的默認(rèn)拷貝構(gòu)造函數(shù)中,內(nèi)置類型是按照字節(jié)方式直接拷貝的,而自定義類型是調(diào)用其拷貝構(gòu)造函數(shù)完成拷貝的
4. 編譯器生成的默認(rèn)拷貝構(gòu)造函數(shù)已經(jīng)可以完成字節(jié)序的值拷貝了,還需要自己顯式實(shí)現(xiàn)嗎?當(dāng)然像日期類這樣的類是沒必要的。那么下面的類呢?驗(yàn)證一下試試?
class Stack
{
public:
Stack(size_t capacity = 3)
{
cout << "Stack(size_t capacity = 3)" << endl;
_a = (int*)malloc(sizeof(int) * capacity);
if (_a == nullptr)
{
perror("malloc fail");
/*exit(-1)*/return;
}
_capacity = capacity;
_top = 0;
}
~Stack()
{
cout << "~Stack()" << endl;
free(_a);
_capacity = _top = 0;
_a = nullptr;
}
Stack(const Stack& stt)
{
//深拷貝
_a = (int*)malloc(sizeof(int) * stt._capacity);
if (_a == nullptr)
{
perror("malloc fail");
exit(-1);//return;
}
memcpy(_a, stt._a, sizeof(int) * stt._top);
_top = stt._top;
_capacity = stt._capacity;
}
private:
int* _a;
int _capacity;
int _top;
};
int main()
{
//拷貝構(gòu)造函數(shù)
Date d1(2023, 10, 22);
func1(d1);
Date d2(d1);
Stack st1;
func2(st1);
Stack st2(st1);
注意:類中如果沒有涉及資源申請時,拷貝構(gòu)造函數(shù)是否寫都可以;一旦涉及到資源申請時,則拷貝構(gòu)造函數(shù)是一定要寫的,否則就是淺拷貝
5. 拷貝構(gòu)造函數(shù)典型調(diào)用場景:
使用已存在對象創(chuàng)建新對象
函數(shù)參數(shù)類型為類類型對象
函數(shù)返回值類型為類類型對象
class Date
{
public:
Date(int year, int minute, int day)
{
cout << "Date(int,int,int):" << this << endl;
}
Date(const Date& d)
{
cout << "Date(const Date& d):" << this << endl;
}
~Date()
{
cout << "~Date():" << this << endl;
}
private:
int _year;
int _month;
int _day;
};
Date Test(Date d)
{
Date temp(d);
return temp;
}
int main()
{
Date d1(2022,1,13);
Test(d1);
return 0;
}
為了提高程序效率,一般對象傳參時,盡量使用引用類型,返回時根據(jù)實(shí)際場景,能用引用盡量使用引用
賦值運(yùn)算符重載
運(yùn)算符重載
C++為了增強(qiáng)代碼的可讀性引入了運(yùn)算符重載,運(yùn)算符重載是具有特殊函數(shù)名的函數(shù),也具有其
返回值類型,函數(shù)名字以及參數(shù)列表,其返回值類型與參數(shù)列表與普通的函數(shù)類似。
函數(shù)名字為:關(guān)鍵字operator后面接需要重載的運(yùn)算符符號
函數(shù)原型:返回值類型 operator操作符(參數(shù)列表)
注意:
不能通過連接其他符號來創(chuàng)建新的操作符:比如operator
重載操作符必須有一個類類型參數(shù)
用于內(nèi)置類型的運(yùn)算符,其含義不能改變,例如:內(nèi)置的整型+,不能改變其含義
作為類成員函數(shù)重載時,其形參看起來比操作數(shù)數(shù)目少1,因?yàn)槌蓡T函數(shù)的第一個參數(shù)為隱
藏的this
.* :: sizeof ?: . 注意以上5個運(yùn)算符不能重載。這個經(jīng)常在筆試選擇題中出現(xiàn)
// 全局的operator==
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//private:
int _year;
int _month;
int _day;
};
// 這里會發(fā)現(xiàn)運(yùn)算符重載成全局的就需要成員變量是公有的,那么問題來了,封裝性如何保證?
// 這里其實(shí)可以用我們后面學(xué)習(xí)的友元解決,或者干脆重載成成員函數(shù)。
bool operator==(const Date& d1, const Date& d2)
{
return d1._year == d2._year
&& d1._month == d2._month
&& d1._day == d2._day;
}
void Test ()
{
Date d1(2018, 9, 26);
Date d2(2018, 9, 27);
cout<<(d1 == d2)<<endl;
}
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// bool operator==(Date* this, const Date& d2)
// 這里需要注意的是,左操作數(shù)是this,指向調(diào)用函數(shù)的對象
bool operator==(const Date& d2)
{
return _year == d2._year;
&& _month == d2._month
&& _day == d2._day;
}
private:
int _year;
int _month;
int _day;
};
以下是我們關(guān)于賦值運(yùn)算符重載的總代碼文章來源:http://www.zghlxwxcb.cn/news/detail-737591.html
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
//int GetYear();
bool operator==(const Date& y)
{
return _year == y._year
&& _month == y._month
&& _day == y._day;
}
bool operator>(const Date& y)
{
if (_year > y._year)
{
return true;
}
else if (_year == y._year && _month > y._month)
{
return true;
}
else if (_year == y._year && _month == y._month && _day > y._day)
{
return true;
}
return false;
}
// d1 - d2
int operator-(const Date& d)
{
// ...
return 0;
}
int GetMonthDay(int year, int month)
{
assert(year >= 1 && month >= 1 && month <= 12);
int monthArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,31 };
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
return 29;
return monthArray[month];
}
// d1 += 100
Date& operator+=(int day)
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month == 13)
{
_year++;
_month = 1;
}
}
return *this;
}
// d1 + 50
/*Date operator+(int day)
{
Date tmp(*this);
tmp._day += day;
while (tmp._day > GetMonthDay(tmp._year, tmp._month))
{
tmp._day -= GetMonthDay(tmp._year, tmp._month);
++tmp._month;
if (tmp._month == 13)
{
tmp._year++;
tmp._month = 1;
}
}
return tmp;
}*/
Date operator+(int day)
{
Date tmp(*this);
tmp += day;
return tmp;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(2023, 10, 22);
bool ret1 = d1 > d2; // d1.operator>(d2) -> d1.operator>(&d1, d2)
bool ret2 = d1 == d2; // d1.operator==(d2) -> d1.operator==(&d1, d2)
// d1.operator>(d2);
// 一個重載哪些運(yùn)算符呢?主要這個運(yùn)算符有沒有意義
// 有意義就可以實(shí)現(xiàn),沒有意義就不要實(shí)現(xiàn)
//d1 + d2;
d1 - d2;
//d1 * d2;
//d1 / d2;
d2 += 50;
d2.Print();
Date ret3 = d1 + 50;
d1.Print();
ret3.Print();
int i = 0, j = 0;
//i += 50;
int ret = j += i += 50;
return 0;
}
賦值運(yùn)算符重載
- 賦值運(yùn)算符重載格式
參數(shù)類型:const T&,傳遞引用可以提高傳參效率
返回值類型:T&,返回引用可以提高返回的效率,有返回值目的是為了支持連續(xù)賦值檢測是否自己給自己賦值
返回*this :要復(fù)合連續(xù)賦值的含義
class Date
{
public :
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date (const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
Date& operator=(const Date& d)
{
if(this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
private:
int _year ;
int _month ;
int _day ;
};
- 賦值運(yùn)算符只能重載成類的成員函數(shù)不能重載成全局函數(shù)
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
int _year;
int _month;
int _day;
};
// 賦值運(yùn)算符重載成全局函數(shù),注意重載成全局函數(shù)時沒有this指針了,需要給兩個參數(shù)
Date& operator=(Date& left, const Date& right)
{
if (&left != &right)
{
left._year = right._year;
left._month = right._month;
left._day = right._day;
}
return left;
}
// 編譯失?。?/span>
// error C2801: “operator =”必須是非靜態(tài)成員
原因:賦值運(yùn)算符如果不顯式實(shí)現(xiàn),編譯器會生成一個默認(rèn)的。此時用戶再在類外自己實(shí)現(xiàn)一個全局的賦值運(yùn)算符重載,就和編譯器在類中生成的默認(rèn)賦值運(yùn)算符重載沖突了,故賦值運(yùn)算符重載只能是類的成員函數(shù)
3. 用戶沒有顯式實(shí)現(xiàn)時,編譯器會生成一個默認(rèn)賦值運(yùn)算符重載,以值的方式逐字節(jié)拷貝。注意:內(nèi)置類型成員變量是直接賦值的,而自定義類型成員變量需要調(diào)用對應(yīng)類的賦值運(yùn)算符重載完成賦值文章來源地址http://www.zghlxwxcb.cn/news/detail-737591.html
class Time
{
public:
Time()
{
_hour = 1;
_minute = 1;
_second = 1;
}
Time& operator=(const Time& t)
{
if (this != &t)
{
_hour = t._hour;
_minute = t._minute;
_second = t._second;
}
return *this;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本類型(內(nèi)置類型)
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定義類型
Time _t;
};
int main()
{
Date d1;
Date d2;
d1 = d2;
return 0;
}
前置++和后置++重載
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 前置++:返回+1之后的結(jié)果
// 注意:this指向的對象函數(shù)結(jié)束后不會銷毀,故以引用方式返回提高效率
Date& operator++()
{
_day += 1;
return *this;
}
// 后置++:
// 前置++和后置++都是一元運(yùn)算符,為了讓前置++與后置++形成能正確重載
// C++規(guī)定:后置++重載時多增加一個int類型的參數(shù),但調(diào)用函數(shù)時該參數(shù)不用傳遞,編譯器
自動傳遞
// 注意:后置++是先使用后+1,因此需要返回+1之前的舊值,故需在實(shí)現(xiàn)時需要先將this保存
一份,然后給this+1
// 而temp是臨時對象,因此只能以值的方式返回,不能返回引用
Date operator++(int)
{
Date temp(*this);
_day += 1;
return temp;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d;
Date d1(2022, 1, 13);
d = d1++; // d: 2022,1,13 d1:2022,1,14
d = ++d1; // d: 2022,1,15 d1:2022,1,15
return 0;
}
到了這里,關(guān)于【C++】:類和對象(中)之拷貝構(gòu)造函數(shù)+賦值運(yùn)算符重載的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!