Halo,這里是Ppeua。平時主要更新C語言,C++,數(shù)據(jù)結(jié)構(gòu)算法…感興趣就關(guān)注我吧!你定不會失望。
0.this指針
在開始本章內(nèi)容之前,先淺淺的了解一下this指針的概念.這對理解后面的內(nèi)容有著很大的幫助.
this指針顧名思義就是這個指針,我們先來看看下面這段很簡單的代碼
class Date{
public:
void print()
{
cout<<date;
}
private:
int date;
};
int main()
{
Date d1;
d1.print();
}
首先創(chuàng)建了一個Date的日期類(這也是我們今天的主角),寫了一個函數(shù):在屏幕上輸出date的具體值,那么this指針體現(xiàn)在哪呢?
每一個函數(shù)調(diào)用的時候都會隱式的傳入一個this指針對象也就是作為成員函數(shù)的形式參數(shù),他指向函數(shù)運行時調(diào)用的對象
所以這段代碼也可以寫成這樣:
class Date{
public:
void print()
{
cout<<this->date;
}
private:
int date;
};
int main()
{
Date d1;
d1.print();
}
this->date = 訪問d1里的date,所以當多個對象互相調(diào)用的時候,編譯器就能分清究竟誰是誰的參數(shù)了this的類型是class *const,也就是無法改變this指向的內(nèi)容(這點也很好理解,如果能輕易改變,那么這個this的定義還有什么用呢?)
那么問題來了 this是存在哪里呢?
- 對象里面
- 棧
- 堆
- 靜態(tài)區(qū)
- 常量區(qū)
很多uu們會選擇第一個選項,對象里面.但我們回顧我們的定義,他是一個 成員函數(shù)的形式參數(shù),那么函數(shù)的形式參數(shù)自然存儲在棧當中(忘記的uu們可以看看這篇文章,會讓你對函數(shù)的創(chuàng)建與銷毀有一個更深層次的理解),所以答案是3
那么再來看看下面的代碼運行會有什么效果呢?
class Date{
public:
void print()
{
cout<<"Hello world";
}
private:
int date;
};
int main()
{
Date *d1=nullptr;
d1->print();
}
雖然d1被初始化為空,但調(diào)用print的時候并沒有訪問到this中成員.所以并不會出現(xiàn)訪問錯誤特點
相對于上面的代碼,下面的代碼有什么問題?
#include<iostream>
using namespace std;
//this
class Date{
public:
void print()
{
cout<<this->date;
}
private:
int date;
};
int main()
{
Date *d1=nullptr;
d1->print();
}
顯而易見輸出內(nèi)容時訪問了this里的內(nèi)容,此時this代表的是d1,d1為空自然this也為空,所以訪問其成員變量就會出現(xiàn)越界訪問的錯誤
1.Class默認成員函數(shù)
了解這個之前來看看為什么要有成員對象.回顧之前所學(xué)的數(shù)據(jù)結(jié)構(gòu):鏈表、棧、隊列…這些數(shù)據(jù)結(jié)構(gòu)使用之前是不是都需要申請內(nèi)存空間.雖然這是必要的,但是每次申請不是很麻煩嘛?且函數(shù)使用完時還需要進行釋放內(nèi)存,但比如我們需要獲取這個棧頂?shù)脑?我們就沒有辦法直接返回(因為返回后程序結(jié)束,內(nèi)存沒有釋放),那返回這類值之前都需要一個變量來存儲嘛?這也太不高級了
所以C++提出了默認成員函數(shù)這一個概念:也就是類里會自動生成一個幫你完成上面的創(chuàng)建,銷毀的工作的函數(shù),當然他的功能還遠遠不止這些
C++中一共有六個默認成員函數(shù)
本篇博客我們重點介紹前三個默認成員函數(shù):構(gòu)造函數(shù)、析構(gòu)函數(shù)、拷貝構(gòu)造函數(shù)
2.構(gòu)造函數(shù)
前面提到,如果每次使用一個類型的時候都需要使用一下Init函數(shù)未免太過繁瑣.所以C++給出了一個構(gòu)造函數(shù)的解決方法先來看看下面這個代碼
class Date{
public:
Date()
{
cout<<"Date";
}
private:
int date;
int monthl;
int year;
};
int main()
{
Date d1;
}
這段代碼僅僅是聲明了一個Date對象d1,并沒有調(diào)用其任何成員函數(shù).但是此時代碼運行結(jié)果會輸出
Date
也就是調(diào)用了Date這個與類同名的函數(shù),這個與類同名的函數(shù)我們就叫做構(gòu)造函數(shù).并不是完成內(nèi)存的創(chuàng)建,僅為對對像屬性的初始化.其有以下幾個特點
- 函數(shù)名與類名相同,其在該對象生命周期之間只會被調(diào)用一次。
- 無返回值。
- 對象實例化時編譯器自動調(diào)用對應(yīng)的構(gòu)造函數(shù)。
- 構(gòu)造函數(shù)可以重載。
前兩個就是字面意思沒什么不好理解的,我們重點來說下后兩個:
調(diào)用規(guī)則:
寫構(gòu)造函數(shù)的方式一般有以下幾種
-
無參數(shù)的構(gòu)造函數(shù)(默認構(gòu)造函數(shù))
class Date{ public: Date() { _date=10; _monthl=12; _year=2023; } void print() { cout<<_date<<endl<<_monthl<<endl<<_year<<endl; } private: int _date; int _monthl; int _year; }; int main() { Date d1; d1.print(); }
在這個函數(shù)中,構(gòu)造函數(shù)并沒有傳入?yún)?shù),所以其叫做無參數(shù)的構(gòu)造函數(shù).,只需要正常聲明就會調(diào)用這個函數(shù)了
-
帶參數(shù)的構(gòu)造函數(shù)
class Date{ public: Date(int date,int month,int year) { _date=date; _monthl=month; _year=year; } void print() { cout<<_date<<endl<<_monthl<<endl<<_year<<endl; } private: int _date; int _monthl; int _year; }; int main() { Date d1(10,20,30); d1.print(); }
在這個函數(shù)中,構(gòu)造函數(shù)傳入了參數(shù),所以叫其 帶參數(shù)的構(gòu)造函數(shù),像調(diào)用函數(shù)一樣聲明即可調(diào)用這個函數(shù)
-
全缺省的構(gòu)造函數(shù)(默認構(gòu)造函數(shù))
class Date{ public: Date(int date=10,int month=10,int year=10) { _date=date; _monthl=month; _year=year; } void print() { cout<<_date<<endl<<_monthl<<endl<<_year<<endl; } private: int _date; int _monthl; int _year; }; int main() { Date d1(10,20,30); d1.print(); }
這和帶參的析構(gòu)函數(shù)差不多,也是一樣的調(diào)用 有參數(shù)的時候按照方式2來傳參,沒有參數(shù)的時候按照方式1來傳參即可,
但注意這種方法不可與無參數(shù)的構(gòu)造函數(shù)同時聲明,因為若沒傳入?yún)?shù)則不知道調(diào)用哪一個了
當然 半缺省的構(gòu)造函數(shù)也是可以的,這里就不過多贅述了.
當用戶沒有自己寫構(gòu)造函數(shù)的時候,編譯器會自動生成一個構(gòu)造函數(shù).與1 3統(tǒng)稱為默認構(gòu)造函數(shù)默認構(gòu)造函數(shù)只能用有一個
但這個函數(shù)將內(nèi)置類型隨機賦值(C++11后打了補丁,允許用戶在聲明時給內(nèi)置類型變量賦值),若有自定義類型,則調(diào)用自定義類型的構(gòu)造函數(shù).
這里解釋下什么是自定義類型和內(nèi)置類型,內(nèi)置類型是語言提供的類型例如:str int double等,而自定義類型則為class struct union等
大多數(shù)情況下我們都需要手寫構(gòu)造函數(shù),所以我們什么時候不需要寫構(gòu)造函數(shù)呢?
- 內(nèi)置類型成員都有自己的缺省值,且初始化符合要求
- 全是自定義類型構(gòu)造函數(shù),且這些類型都定義了默認構(gòu)造
3.析構(gòu)函數(shù)
析構(gòu)函數(shù)與構(gòu)造函數(shù)的功能剛好相反,其也不涉及對對像本身的銷毀,而是對對象銷毀完后"殘局的處理"
class Date{
public:
Date()
{
a=(int*)malloc(sizeof(int)*4);
}
~Date()
{
cout<<"~Date";
free(a);
a=nullptr;
}
private:
int *a=nullptr;
};
當Date創(chuàng)建的對象所在的棧幀被銷毀時,會自動調(diào)用這個析構(gòu)函數(shù)來處理殘局.
析構(gòu)函數(shù)的特點如下:
- 其名字為~+類名
- 無參數(shù)無返回類型,不支持重載
- 對象生命周期結(jié)束的時候,會自動調(diào)用析構(gòu)函數(shù)
當有動態(tài)申請空間的時候,我們就需要手寫析構(gòu)函數(shù).那我們什么時候不需要寫析構(gòu)函數(shù)呢?
- 沒有動態(tài)申請的資源
- 需要釋放資源的成員都是自定義類型,
4.拷貝構(gòu)造函數(shù)
當我們想用一個對象的值去初始化另一個對象的時候,我們該如何去做呢?
Date d1=d2;
C++規(guī)定了用一個對象去初始化另一個對象的時候.需要調(diào)用拷貝構(gòu)造函數(shù).拷貝構(gòu)造函數(shù)時構(gòu)造函數(shù)的重載,所以其也滿足構(gòu)造函數(shù)的規(guī)則
class Date{
public:
Date(int date,int month,int year)
{
_date=date;
_monthl=month;
_year=year;
}
Date(const Date &d)
{
cout<<"copy Date";
_date=d._date;
_monthl=d._monthl;
_year=d._year;
}
void print()
{
cout<<_date<<endl<<_monthl<<endl<<_year<<endl;
}
private:
int _date;
int _monthl;
int _year;
};
int main()
{
Date d1(11,12,123);
Date d2=d1;
d2.print();
}
此時d2的初始化調(diào)用了拷貝構(gòu)造函數(shù).為什么拷貝構(gòu)造函數(shù)需要的形參為值的引用呢
因為若不傳值的引用,拷貝構(gòu)造函數(shù)的形式參數(shù)仍為一個Date類型,傳值的過程仍然是在初始化拷貝一個類對象,又會去調(diào)用其拷貝構(gòu)造函數(shù),之后就無限套娃.陷入了循環(huán).
所以形參需要是引用
若沒有自己寫出拷貝函數(shù),其調(diào)用規(guī)則為:
- 內(nèi)置類型成員完成值拷貝/淺拷貝
- 自定義類型成員會調(diào)用他自己的拷貝構(gòu)造函數(shù)
什么是深淺拷貝呢?這里淺淺的提一下
淺拷貝會對值進行復(fù)制,若為數(shù)組,則會創(chuàng)建一個指針指向原地址.也就是兩個指針指向同一塊空間
深拷貝則會創(chuàng)建兩個一模一樣的變量
所以淺拷貝在拷貝空間的時候會出現(xiàn)問題(兩個變量共用一塊空間),此時我們就需要自己手寫拷貝構(gòu)造實現(xiàn)深拷貝
本篇文章結(jié)束啦,感興趣的uu們可以關(guān)注一下文章來源:http://www.zghlxwxcb.cn/news/detail-433044.html
諸君,山頂見!文章來源地址http://www.zghlxwxcb.cn/news/detail-433044.html
到了這里,關(guān)于【C++技能樹】類的六個成員函數(shù)Ⅰ --構(gòu)造、析構(gòu)、拷貝構(gòu)造函數(shù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!