=========================================================================
個(gè)人主頁(yè)點(diǎn)擊直達(dá):小白不是程序媛
C++系列專(zhuān)欄:C++頭疼記
=========================================================================
目錄
前言
類(lèi)的6個(gè)默認(rèn)成員函數(shù)
構(gòu)造函數(shù)
概念
構(gòu)造函數(shù)的特性
析構(gòu)函數(shù)
概念
析構(gòu)函數(shù)特性
拷貝構(gòu)造函數(shù)
概念
拷貝構(gòu)造函數(shù)特性
總結(jié)
前言
上篇文章我們對(duì)于C++中的類(lèi)有了初步的認(rèn)識(shí)和了解,在最后通過(guò)日期類(lèi)的類(lèi)型對(duì)于this指針有了一定的了解,今天我們繼續(xù)深入C++類(lèi)和對(duì)象的了解?。?!
類(lèi)的6個(gè)默認(rèn)成員函數(shù)
如果一個(gè)類(lèi)中什么成員都沒(méi)有,簡(jiǎn)稱(chēng)為空類(lèi)。
空類(lèi)中真的什么都沒(méi)有嗎?并不是,任何類(lèi)在什么都不寫(xiě)時(shí),編譯器會(huì)自動(dòng)生成以下6個(gè)默認(rèn)成員
函數(shù)。
默認(rèn)成員函數(shù):用戶(hù)沒(méi)有顯式實(shí)現(xiàn),編譯器會(huì)生成的成員函數(shù)稱(chēng)為默認(rèn)成員函數(shù)。
class Date { };
構(gòu)造函數(shù)
-
概念
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Init(2023, 10, 27);
d1.Print();
return 0;
}
對(duì)于Date類(lèi),可以通過(guò) Init 公有方法給對(duì)象設(shè)置日期,但如果每次創(chuàng)建對(duì)象時(shí)都調(diào)用該方法設(shè)置
信息,未免有點(diǎn)麻煩,那能否在對(duì)象創(chuàng)建時(shí),就將信息設(shè)置進(jìn)去呢?
構(gòu)造函數(shù)是一個(gè)特殊的成員函數(shù),名字與類(lèi)名相同,創(chuàng)建類(lèi)類(lèi)型對(duì)象時(shí)由編譯器自動(dòng)調(diào)用,以保證每個(gè)數(shù)據(jù)成員都有 一個(gè)合適的初始值,并且在對(duì)象整個(gè)生命周期內(nèi)只調(diào)用一次。
-
構(gòu)造函數(shù)的特性
構(gòu)造函數(shù)是特殊的成員函數(shù),需要注意的是,構(gòu)造函數(shù)雖然名稱(chēng)叫構(gòu)造,但是構(gòu)造函數(shù)的主要任
務(wù)并不是開(kāi)空間創(chuàng)建對(duì)象,而是初始化對(duì)象。
其特征如下:
1. 函數(shù)名與類(lèi)名相同。
2. 無(wú)返回值。
3. 對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)。
4. 構(gòu)造函數(shù)可以重載。
//構(gòu)造函數(shù)
class Date
{
public:
//無(wú)參構(gòu)造函數(shù)
Date()
{
_year = 1;
_month = 1;
_day = 1;
}
//有參構(gòu)造函數(shù)
//重載
//缺省
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
//調(diào)用無(wú)參構(gòu)造函數(shù)
//Date d1;
//d1.Print();
//調(diào)用有參構(gòu)造函數(shù)
Date d2(2023, 10, 27);
d2.Print();
return 0;
}
注意:如果通過(guò)無(wú)參構(gòu)造函數(shù)創(chuàng)建對(duì)象時(shí),對(duì)象后面不用跟括號(hào),否則就成了函數(shù)聲明
5. 如果類(lèi)中沒(méi)有顯式定義構(gòu)造函數(shù),則C++編譯器會(huì)自動(dòng)生成一個(gè)無(wú)參的默認(rèn)構(gòu)造函數(shù),一旦用戶(hù)顯式定義編譯器將不再生成。
class Date
{
public:
/*Date()
{
_year = 1;
_month = 1;
_day = 1;
}*/
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Print();
return 0;
}
將Date類(lèi)中構(gòu)造函數(shù)屏蔽后,代碼可以通過(guò)編譯,因?yàn)榫幾g器生成了一個(gè)無(wú)參的默認(rèn)構(gòu)造函數(shù)
6. 關(guān)于編譯器生成的默認(rèn)成員函數(shù),很多童鞋會(huì)有疑惑:不實(shí)現(xiàn)構(gòu)造函數(shù)的情況下,編譯器會(huì)生成默認(rèn)的構(gòu)造函數(shù)。但是看起來(lái)默認(rèn)構(gòu)造函數(shù)又沒(méi)什么用?d對(duì)象調(diào)用了編譯器生成的默
認(rèn)構(gòu)造函數(shù),但是d對(duì)象_year/_month/_day,依舊是隨機(jī)值。也就說(shuō)在這里編譯器生成的默認(rèn)構(gòu)造函數(shù)并沒(méi)有什么用??
解答:C++把類(lèi)型分成內(nèi)置類(lèi)型(基本類(lèi)型)和自定義類(lèi)型。內(nèi)置類(lèi)型就是語(yǔ)言提供的數(shù)據(jù)類(lèi)
型,如:int/char...,自定義類(lèi)型就是我們使用class/struct/union等自己定義的類(lèi)型,看看
下面的程序,就會(huì)發(fā)現(xiàn)編譯器生成默認(rèn)的構(gòu)造函數(shù)會(huì)對(duì)自定類(lèi)型成員_t調(diào)用的它的默認(rèn)成員
函數(shù)。
class Time
{
public:
Time()
{
_hour = 1;
_minute = 1;
_secend = 1;
}
void Print()
{
cout << _hour << "-" << _minute << "-" << _secend << endl;
}
private:
int _hour;
int _minute;
int _secend;
};
class Date
{
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
//內(nèi)置類(lèi)型
int _year;
int _month;
int _day;
//自定義類(lèi)型
Time _t;
};
int main()
{
Date d1;
return 0;
}
注意:C++11 中針對(duì)內(nèi)置類(lèi)型成員不初始化的缺陷,又打了補(bǔ)丁,即:內(nèi)置類(lèi)型成員變量在
類(lèi)中聲明時(shí)可以給默認(rèn)值。
class Time
{
public:
Time()
{
_hour = 1;
_minute = 1;
_secend = 1;
}
void Print()
{
cout << _hour << "-" << _minute << "-" << _secend << endl;
}
private:
int _hour;
int _minute;
int _secend;
};
class Date
{
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
//內(nèi)置類(lèi)型
int _year=1;
int _month=1;
int _day=1;
//自定義類(lèi)型
Time _t;
};
int main()
{
Date d1;
return 0;
}
?
7. 無(wú)參的構(gòu)造函數(shù)和全缺省的構(gòu)造函數(shù)都稱(chēng)為默認(rèn)構(gòu)造函數(shù),并且默認(rèn)構(gòu)造函數(shù)只能有一個(gè)。
注意:無(wú)參構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù)、我們沒(méi)寫(xiě)編譯器默認(rèn)生成的構(gòu)造函數(shù),都可以認(rèn)為
是默認(rèn)構(gòu)造函數(shù)。?
析構(gòu)函數(shù)
-
概念
通過(guò)前面構(gòu)造函數(shù)的學(xué)習(xí),我們知道一個(gè)對(duì)象是怎么來(lái)的,那一個(gè)對(duì)象又是怎么沒(méi)呢的?
析構(gòu)函數(shù):與構(gòu)造函數(shù)功能相反,析構(gòu)函數(shù)不是完成對(duì)對(duì)象本身的銷(xiāo)毀,局部對(duì)象銷(xiāo)毀工作是由
編譯器完成的。而對(duì)象在銷(xiāo)毀時(shí)會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù),完成對(duì)象中資源的清理工作。
class stack
{
public:
stack(int capacity = 3)
{
_a = (int*)malloc(sizeof(int) * capacity);
if (nullptr == _a)
{
perror("malloc fail");
exit(-1);
}
_capacity = capacity;
_top = 0;
}
void Destory()
{
free(_a);
_capacity = 0;
_top = 0;
}
private:
int* _a;
int _capacity;
int _top;
};
int main()
{
stack s1;
return 0;
}
-
析構(gòu)函數(shù)特性
析構(gòu)函數(shù)是特殊的成員函數(shù),其特征如下:
1. 析構(gòu)函數(shù)名是在類(lèi)名前加上字符 ~。
2. 無(wú)參數(shù)無(wú)返回值類(lèi)型。
3. 一個(gè)類(lèi)只能有一個(gè)析構(gòu)函數(shù)。若未顯式定義,系統(tǒng)會(huì)自動(dòng)生成默認(rèn)的析構(gòu)函數(shù)。注意:析構(gòu)函數(shù)不能重載
4. 對(duì)象生命周期結(jié)束時(shí),C++編譯系統(tǒng)系統(tǒng)自動(dòng)調(diào)用析構(gòu)函數(shù)。
class stack
{
public:
stack(int capacity = 3)
{
_a = (int*)malloc(sizeof(int) * capacity);
if (nullptr == _a)
{
perror("malloc fail");
exit(-1);
}
_capacity = capacity;
_top = 0;
}
~stack()
{
free(_a);
_capacity = 0;
_top = 0;
}
private:
int* _a;
int _capacity;
int _top;
};
int main()
{
stack s1;
return 0;
}
5. 關(guān)于編譯器自動(dòng)生成的析構(gòu)函數(shù),是否會(huì)完成一些事情呢?下面的程序我們會(huì)看到,編譯器生成的默認(rèn)析構(gòu)函數(shù),對(duì)自定類(lèi)型成員調(diào)用它的析構(gòu)函數(shù)。
class Time
{
public:
~Time()
{
cout << "~Time()" << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本類(lèi)型(內(nèi)置類(lèi)型)
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定義類(lèi)型
Time _t;
};
int main()
{
Date d;
return 0;
}
程序運(yùn)行結(jié)束后輸出:~Time()
在main方法中根本沒(méi)有直接創(chuàng)建Time類(lèi)的對(duì)象,為什么最后會(huì)調(diào)用Time類(lèi)的析構(gòu)函數(shù)?
因?yàn)椋簃ain方法中創(chuàng)建了Date對(duì)象d,而d中包含4個(gè)成員變量,其中_year, _month,_day三個(gè)是內(nèi)置類(lèi)型成員,銷(xiāo)毀時(shí)不需要資源清理,最后系統(tǒng)直接將其內(nèi)存回收即可;而_t是Time類(lèi)對(duì)象,所以在d銷(xiāo)毀時(shí),要將其內(nèi)部包含的Time類(lèi)的_t對(duì)象銷(xiāo)毀,所以要調(diào)用Time類(lèi)的析構(gòu)函數(shù)。但是:main函數(shù)中不能直接調(diào)用Time類(lèi)的析構(gòu)函數(shù),實(shí)際要釋放的是Date類(lèi)對(duì)象,所以編譯器會(huì)調(diào)用Date 類(lèi)的析構(gòu)函數(shù),而Date沒(méi)有顯式提供,則編譯器會(huì)給Date類(lèi)生成一個(gè)默認(rèn)的析構(gòu)函數(shù),目的是在其內(nèi)部調(diào)用Time類(lèi)的析構(gòu)函數(shù),即當(dāng)Date對(duì)象銷(xiāo)毀時(shí),要保證其內(nèi)部每個(gè)自定義對(duì)象都可以正確銷(xiāo)毀main函數(shù)中并沒(méi)有直接調(diào)用Time類(lèi)析構(gòu)函數(shù),而是顯式調(diào)用編譯器為Date類(lèi)生成的默認(rèn)析構(gòu)函數(shù)
注意:創(chuàng)建哪個(gè)類(lèi)的對(duì)象則調(diào)用該類(lèi)的析構(gòu)函數(shù),銷(xiāo)毀那個(gè)類(lèi)的對(duì)象則調(diào)用該類(lèi)的析構(gòu)函數(shù)?
6. 如果類(lèi)中沒(méi)有申請(qǐng)資源時(shí),析構(gòu)函數(shù)可以不寫(xiě),直接使用編譯器生成的默認(rèn)析構(gòu)函數(shù),比如Date類(lèi);有資源申請(qǐng)時(shí),一定要寫(xiě),否則會(huì)造成資源泄漏,比如Stack類(lèi)。
拷貝構(gòu)造函數(shù)
-
概念
在現(xiàn)實(shí)生活中,可能存在一個(gè)與你一樣的自己,我們稱(chēng)其為雙胞胎。
那在創(chuàng)建對(duì)象時(shí),可否創(chuàng)建一個(gè)與已存在對(duì)象一某一樣的新對(duì)象呢?
拷貝構(gòu)造函數(shù):只有單個(gè)形參,該形參是對(duì)本類(lèi)類(lèi)型對(duì)象的引用(一般常用const修飾),在用已存
在的類(lèi)類(lèi)型對(duì)象創(chuàng)建新對(duì)象時(shí)由編譯器自動(dòng)調(diào)用。
-
拷貝構(gòu)造函數(shù)特性
拷貝構(gòu)造函數(shù)也是特殊的成員函數(shù),其特征如下:
1. 拷貝構(gòu)造函數(shù)是構(gòu)造函數(shù)的一個(gè)重載形式。2. 拷貝構(gòu)造函數(shù)的參數(shù)只有一個(gè)且必須是類(lèi)類(lèi)型對(duì)象的引用,使用傳值方式編譯器直接報(bào)錯(cuò),因?yàn)闀?huì)引發(fā)無(wú)窮遞歸調(diào)用。
class Date
{
public :
Date()
{
_year = 1;
_month = 1;
_day = 1;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
cout << "拷貝構(gòu)造函數(shù)的調(diào)用" << endl;
}
private :
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(d1);
return 0;
}
?
3. 若未顯式定義,編譯器會(huì)生成默認(rèn)的拷貝構(gòu)造函數(shù)。 默認(rèn)的拷貝構(gòu)造函數(shù)對(duì)象按內(nèi)存存儲(chǔ)按
字節(jié)序完成拷貝,這種拷貝叫做淺拷貝,或者值拷貝。
class Time
{
public:
Time()
{
_hour = 1;
_minute = 1;
_secend = 1;
}
Time(const Time& T)
{
_hour = T._hour;
_minute = T._minute;
_secend = T._secend;
cout << "Time拷貝構(gòu)造的調(diào)用" << endl;
}
void Print()
{
cout << _hour << "-" << _minute << "-" << _secend << endl;
}
private:
int _hour;
int _minute;
int _secend;
};
class Date
{
public:
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year=2023;
int _month=10;
int _day=27;
Time _t;
};
int main()
{
Date d1;
Date d2(d1);
return 0;
}
注意:在編譯器生成的默認(rèn)拷貝構(gòu)造函數(shù)中,內(nèi)置類(lèi)型是按照字節(jié)方式直接拷貝的,而自定
義類(lèi)型是調(diào)用其拷貝構(gòu)造函數(shù)完成拷貝的。
4. 編譯器生成的默認(rèn)拷貝構(gòu)造函數(shù)已經(jīng)可以完成字節(jié)序的值拷貝了,還需要自己顯式實(shí)現(xiàn)嗎?
當(dāng)然像日期類(lèi)這樣的類(lèi)是沒(méi)必要的。那么下面的類(lèi)呢?驗(yàn)證一下試試?
class stack
{
public:
stack(int capacity = 3)
{
_a = (int*)malloc(sizeof(int) * capacity);
if (nullptr == _a)
{
perror("malloc fail");
}
_capacity = capacity;
this->_top = 0;
}
stack(const stack& stt)
{
_a = stt._a;
_capacity = stt._capacity;
_top = stt._top;
}
~stack()
{
free(_a);
this->_a = nullptr;
this->_capacity = 0;
this->_top = 0;
}
private :
int* _a;
int _capacity;
int _top;
};
class MyQueue
{
stack _pushstack;
stack _popshtack;
};
int main()
{
MyQueue q1;
MyQueue q2(q1);
return 0;
}
注意:類(lèi)中如果沒(méi)有涉及資源申請(qǐng)時(shí),拷貝構(gòu)造函數(shù)是否寫(xiě)都可以;一旦涉及到資源申請(qǐng)
時(shí),則拷貝構(gòu)造函數(shù)是一定要寫(xiě)的,否則就是淺拷貝。
該如何解決這個(gè)問(wèn)題呢?我給出一段代碼
class stack
{
public:
stack(int capacity = 3)
{
_a = (int*)malloc(sizeof(int) * capacity);
if (nullptr == _a)
{
perror("malloc fail");
}
_capacity = capacity;
this->_top = 0;
}
stack(const stack& stt)
{
this->_a = (int*)malloc(sizeof(int) * stt._capacity);
if (nullptr == _a)
{
perror("malloc fail");
}
memcpy(this->_a, stt._a, sizeof(int) * stt._top);
this->_capacity = stt._capacity;
this->_top = stt._top;
}
~stack()
{
free(_a);
this->_a = nullptr;
this->_capacity = 0;
this->_top = 0;
}
private:
int* _a;
int _capacity;
int _top;
};
class MyQueue
{
stack _pushstack;
stack _popshtack;
};
int main()
{
MyQueue q1;
MyQueue q2(q1);
return 0;
}
?深拷貝:本質(zhì)拷貝指向的資源,讓我跟你有一樣大的空間,一樣的值
5. 拷貝構(gòu)造函數(shù)典型調(diào)用場(chǎng)景:
- 使用已存在對(duì)象創(chuàng)建新對(duì)象
- 函數(shù)參數(shù)類(lèi)型為類(lèi)類(lèi)型對(duì)象
- 函數(shù)返回值類(lèi)型為類(lèi)類(lèi)型對(duì)象
為了提高程序效率,一般對(duì)象傳參時(shí),盡量使用引用類(lèi)型,返回時(shí)根據(jù)實(shí)際場(chǎng)景,能用引用
盡量使用引用。?
總結(jié)
- 構(gòu)造函數(shù)對(duì)于自定義類(lèi)型會(huì)調(diào)用其構(gòu)造函數(shù),對(duì)于內(nèi)置類(lèi)型 不做任何處理,在C++11中,內(nèi)置類(lèi)型在類(lèi)里可以初始化;
- 析構(gòu)函數(shù)對(duì)于自定義類(lèi)型會(huì)調(diào)用其析構(gòu)函數(shù),對(duì)于內(nèi)置類(lèi)型銷(xiāo)毀時(shí)不需要資源清理,最后系統(tǒng)直接將其內(nèi)存回收即可;
- 拷貝構(gòu)造函數(shù)對(duì)于自定義類(lèi)型調(diào)用其拷貝構(gòu)造函數(shù),對(duì)于內(nèi)置類(lèi)型完成值拷貝;
今天的文章就到這里就結(jié)束了,希望大家看完有所收獲,也希望大家留言指出我文章中出現(xiàn)的內(nèi)容,同時(shí)也感謝各位看官的三連支持,你們的支持就是我更新的動(dòng)力?。?!?
?文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-734818.html
下篇預(yù)告:賦值重載和取地址重載文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-734818.html
到了這里,關(guān)于【C++初階】類(lèi)和對(duì)象——構(gòu)造函數(shù)&&析構(gòu)函數(shù)&&拷貝構(gòu)造函數(shù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!