??個(gè)人主頁:?? :???初階牛???
??推薦專欄1: ??????C語言初階
??推薦專欄2: ??????C語言進(jìn)階
??個(gè)人信條: ??知行合一
??本篇簡介:>:講解C++中有關(guān)類和對(duì)象的介紹,本篇是中篇的第結(jié)尾篇文章,講解拷貝構(gòu)造,運(yùn)算符重載以及取地址重載符.
金句分享:
?別在最好的年紀(jì),辜負(fù)了最好的自己.?
一、“拷貝構(gòu)造函數(shù)”
拷貝構(gòu)造函數(shù):
只有單個(gè)形參,該形參是對(duì)本類類型對(duì)象的引用(一般常用const修飾),在用已存在的類類型對(duì)象創(chuàng)建新對(duì)象時(shí)由編譯器自動(dòng)調(diào)用。
2.1 自動(dòng)生成的"拷貝構(gòu)造函數(shù)"
假設(shè)哦我們需要?jiǎng)?chuàng)建兩個(gè)一模一樣的對(duì)象A
和B
.
那我們可以先創(chuàng)建一個(gè)對(duì)象A
,再通過將A
作為參數(shù),傳給B
進(jìn)行初始化,
即一個(gè)自定義類型實(shí)例化出的對(duì)象(B
)用另一個(gè)該類型實(shí)例化出的對(duì)象(A
)進(jìn)行初始化.
class Date
{
public:
Date(int year = 2020, int month = 1, int day = 1)//全缺省構(gòu)造函數(shù)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date A(2023, 7, 20);
A.Print();
printf("\n");
Date B(A);//會(huì)調(diào)用系統(tǒng)生成的拷貝構(gòu)造
B.Print();
return 0;
}
運(yùn)行結(jié)果:
2023-7-20
2023-7-20
其實(shí)拷貝構(gòu)造函數(shù)就是構(gòu)造函數(shù)的一種重載形式,他也是六大天選之子之一,沒有顯式定義時(shí),編譯器也會(huì)自動(dòng)生成,但是只會(huì)完成"淺拷貝
"(下面講)…
2.2 自定義"拷貝構(gòu)造函數(shù)"
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
class Date
{
public:
Date(int year = 2020, int month = 1, int day = 1)//全缺省構(gòu)造函數(shù)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)//拷貝構(gòu)造函數(shù)
{
cout << "拷貝構(gòu)造" << endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2023, 7, 20);
d1.Print();
printf("\n");
Date d2(d1);
d2.Print();
return 0;
}
我們發(fā)現(xiàn)Date(const Date& d)
這里使用了引用傳參,如果直接傳參會(huì)怎樣呢?
為什么會(huì)報(bào)錯(cuò)呢?
void test(int a)
{
}
void test(Date d1)
{
}
int main()
{
Date d1(2023, 7, 20);
test(2);
test(d1);
return 0;
}
這段代碼會(huì)調(diào)用Date
類的拷貝構(gòu)造.
對(duì)于自定義類型作為參數(shù)時(shí),必須調(diào)用該類型的拷貝構(gòu)造函數(shù).
所以可以回答上面的問題了.
所以拷貝構(gòu)造函數(shù)傳參時(shí)采用引用傳參,這樣就避免了傳參時(shí)調(diào)用拷貝構(gòu)造.
2.3 深淺拷貝?
前面在介紹編譯器自動(dòng)生成的"拷貝構(gòu)造函數(shù)"時(shí),提到了淺拷貝,那什么是淺拷貝呢?
淺拷貝:按內(nèi)存存儲(chǔ)按字節(jié)序完成拷貝,這種拷貝叫做淺拷貝,或者值拷貝
深拷貝:
示例:
棧類中沒有顯式定義拷貝構(gòu)造函數(shù),編譯器自動(dòng)生成的拷貝構(gòu)造是淺拷貝帶來的問題.
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
typedef int DataType;
class Stack
{
public:
Stack(int capacity=5)//全缺省構(gòu)造函數(shù)
{
cout << "Stack" << endl;
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (NULL == _array)
{
perror("malloc申請(qǐng)空間失敗!!!");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(DataType data)//壓棧操作
{
CheckCapacity();
_array[_size] = data;
_size++;
}
~Stack()//析構(gòu)函數(shù)
{
cout << "~Stack"<< endl;
if (_array)
{
free(_array);
_array = NULL;
_capacity = 0;
_size = 0;
}
}
private:
void CheckCapacity()
{
if (_size == _capacity)
{
int newcapacity = _capacity * 2;
DataType* temp = (DataType*)realloc(_array, newcapacity *
sizeof(DataType));
if (temp == NULL)
{
perror("realloc申請(qǐng)空間失敗!!!");
return;
}
_array = temp;
_capacity = newcapacity;
}
}
private:
DataType* _array;
int _capacity;
int _size;
};
int main()
{
Stack s1;
s1.Push(1);
s1.Push(2);
s1.Push(3);
s1.Push(4);
Stack s2(s1);//這條語句會(huì)報(bào)錯(cuò).
return 0;
}
運(yùn)行結(jié)果:
原因:
因?yàn)榫幾g器默認(rèn)生成的拷貝構(gòu)造是淺拷貝,這里兩個(gè)對(duì)象的_array
也就指向了同一塊內(nèi)存空間,但是兩個(gè)對(duì)象的聲生命周期結(jié)束時(shí),會(huì)調(diào)用各自的析構(gòu)函數(shù),這也就導(dǎo)致對(duì)同一塊空間進(jìn)行了釋放操作.
解決方法:
顯示定義一個(gè)拷貝構(gòu)造函數(shù).
Stack(const Stack& S)//深拷貝
{
_array = (int*)malloc(sizeof(int) * S._capacity);
if (NULL == _array)
{
perror("malloc申請(qǐng)空間失敗!!!");
return;
}
memcpy(S._array,_array,sizeof(int)*S._size);
_capacity = S._capacity;
_size = S._size;
}
總結(jié):
拷貝構(gòu)造使用場景:
- 使用已存在對(duì)象創(chuàng)建新對(duì)象
- 函數(shù)參數(shù)類型為類類型對(duì)象
- 函數(shù)返回值類型為類類型對(duì)象
- 拷貝構(gòu)造函數(shù)是構(gòu)造函數(shù)的一個(gè)重載形式。
- 拷貝構(gòu)造函數(shù)的參數(shù)只有一個(gè)且必須是類類型對(duì)象的引用,使用傳值方式編譯器直接報(bào)錯(cuò),因?yàn)闀?huì)引發(fā)無窮遞歸調(diào)用。
- 當(dāng)一個(gè)對(duì)象作為參數(shù)傳遞給函數(shù)時(shí),拷貝構(gòu)造函數(shù)會(huì)被調(diào)用來創(chuàng)建一個(gè)新的對(duì)象,該新對(duì)象與傳遞的對(duì)象具有相同的屬性和屬性值,但是它們在內(nèi)存中是獨(dú)立的。
- 若未顯式定義,編譯器會(huì)生成默認(rèn)的拷貝構(gòu)造函數(shù)。 默認(rèn)的拷貝構(gòu)造函數(shù)對(duì)象按內(nèi)存存儲(chǔ)按字節(jié)序完成拷貝,這種拷貝叫做淺拷貝,或者值拷貝.
二、賦值運(yùn)算符重載(“=”)
2.1 運(yùn)算符重載的介紹
class Date//日期類
{
public:
Date(int year = 2023, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
void test1()
{
Date d1(2023, 7, 28);
Date d2;
if (d2 == d1)
{
cout << "d1=d2";
}
if (d1 < d2)
{
cout << "d1<d2";
}
}
自定義類型是無法像內(nèi)置類型一樣比較大小和使用一些常規(guī)運(yùn)算符的.
為什么呢?
因?yàn)?mark>自定義類型是用戶自己定義的,編譯器不知道該如何進(jìn)行比較.那編譯器太笨了吧,日期按 年-月-日依次比較不就行了?
個(gè)人理解:
- 格局打開,如果是別的類呢?比如:
person
是按名字還是按職位,還是按什么?你不告訴編譯器如何比較,編譯器也很無奈,不敢瞎搞的. - 編譯器咋知道你
year
是年,要是牛牛用nian
來命名,他也能識(shí)別出來是年嗎?
綜上,自定義類型如何進(jìn)行運(yùn)算比較,只有用戶自己知道,所以用戶需要自己來設(shè)計(jì)規(guī)則.
C++
為了增強(qiáng)代碼的可讀性引入了運(yùn)算符重載,運(yùn)算符重載是具有特殊函數(shù)名的函數(shù),也具有其返回值類型.
函數(shù)名:關(guān)鍵字operator
+需要重載的運(yùn)算符符號(hào)。
operator+ 需要重載的運(yùn)算符
注意事項(xiàng):
-
不能通過連接其他符號(hào)來創(chuàng)建新的操作符:
示例:operator@
-
重載操作符必須有一個(gè)類類型參數(shù)
運(yùn)算符重載是通過類的成員函數(shù)或全局函數(shù)來實(shí)現(xiàn)的,而這些函數(shù)必須具有特定的參數(shù)列表。
對(duì)于成員函數(shù)的重載操作符,至少需要一個(gè)類類型參數(shù),它表示操作符的左操作數(shù)。例如,對(duì)于二元操作符(如 +、-、* 等),成員函數(shù)的參數(shù)列表通常還包括一個(gè)非常量引用或常量引用,表示操作符的右操作數(shù)。 -
用于內(nèi)置類型的運(yùn)算符,其含義不能改變:
例如:內(nèi)置的整型*
不要實(shí)現(xiàn)為了/
,害人是不對(duì)的. -
作為類成員函數(shù)重載時(shí),其形參看起來比操作數(shù)數(shù)目少1一個(gè),因?yàn)槌蓡T函數(shù)的第一個(gè)參數(shù)為隱藏的this .
-
注意以下5個(gè)運(yùn)算符不能重載?!?code>.*” (點(diǎn)星) 、"
::
"sizeof
? :
.
在C++中,有一些操作符是不能被重載的,包括以下幾種情況:
-
::
(作用域解析操作符):作用域解析操作符用于指定命名空間、類或結(jié)構(gòu)的作用域,并訪問其成員。它不能被重載,因?yàn)樗暮x在語言中已經(jīng)固定不可更改。 -
.*
(指針到成員操作符)和->*
(指向成員指針的操作符):這些操作符用于訪問類的成員指針。它們存儲(chǔ)了一個(gè)指向類成員的指針,并用于在運(yùn)行時(shí)訪問該成員。它們也不能被重載。 -
sizeof
(大小操作符):sizeof
操作符用于獲取一個(gè)對(duì)象或類型的大?。ㄒ宰止?jié)為單位)。它是一個(gè)編譯時(shí)的操作符,不能在運(yùn)行時(shí)被重載。因?yàn)樵诰幾g時(shí)就已經(jīng)確定了對(duì)象或類型的大小。 -
?
:(條件操作符,即三目運(yùn)算符):條件操作符是一個(gè)三元操作符,用于根據(jù)條件選擇不同的表達(dá)式。它不能被重載,因?yàn)樗恼Z法和含義已經(jīng)在語言中定義好了。 -
.
在C++
中,點(diǎn)操作符(“.
”)是用來訪問對(duì)象的成員的,而它本身是不能被重載的。點(diǎn)操作符的行為在語言中是固定的,無法通過重載來改變。
2.2 賦值運(yùn)算符重載:
(1)編譯器自動(dòng)生成的 “賦值運(yùn)算符重載”
class Date//日期類
{
public:
Date(int year = 2023, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
void test1()
{
Date d1(2023, 7, 28);
Date d2;
d1.print();
d2.print();
cout << endl;
d2 = d1;
d1.print();
d2.print();
}
int main()
{
test1();
return 0;
}
賦值運(yùn)算符只能重載成類的成員函數(shù)不能重載成全局函數(shù):
原因:
賦值運(yùn)算符如果不顯式實(shí)現(xiàn)(自己定義),編譯器會(huì)生成一個(gè)默認(rèn)的。此時(shí)用戶再在類外自己實(shí)現(xiàn)一個(gè)全局的賦值運(yùn)算符重載,就和編譯器在類中生成的默認(rèn)賦值運(yùn)算符重載沖突了,故賦值運(yùn)算符重載只能是類的成員函數(shù)。
那編譯器會(huì)生成一個(gè)默認(rèn)賦值運(yùn)算符重載會(huì)做什么事情呢?
以值的方式逐字節(jié)拷貝。注意:內(nèi)置類型成員變量是直接賦值的,而自定義類型成員變量需要調(diào)用對(duì)應(yīng)類的賦值運(yùn)算符重載完成賦值。
當(dāng)然對(duì)于日期類這種只需要淺拷貝的類來說,編譯器默認(rèn)生成就已經(jīng)足夠了,但是像stack
類,同樣引發(fā)深淺拷貝的問題.
三、最后的兩個(gè)天選之子
哈哈哈,期待到最后的兩個(gè)默認(rèn)成員函數(shù)其實(shí)沒什么要講解的.
- 取地址操作符重載
operator&()
- const取地址操作符重載
operator&()const
這兩個(gè)默認(rèn)成員函數(shù)一般不用重新定義 ,編譯器默認(rèn)會(huì)生成。
class Date
{
public :
Date* operator&()
{
return this ;
}
const Date* operator&()const
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};
這兩個(gè)運(yùn)算符一般不需要重載,使用編譯器生成的默認(rèn)取地址的重載即可除非你想搞點(diǎn)特殊的,返回一個(gè)特定的特殊地址.文章來源:http://www.zghlxwxcb.cn/news/detail-635623.html
本篇內(nèi)容到此講解完了,后續(xù)介紹日期類
的具體實(shí)現(xiàn),方便大家更好的理解類和對(duì)象的知識(shí),實(shí)戰(zhàn)才能鍛煉水平哦.文章來源地址http://www.zghlxwxcb.cn/news/detail-635623.html
到了這里,關(guān)于C++的六大“天選之子“拷貝構(gòu)造與與運(yùn)算符重載的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!