在58同城做網(wǎng)站怎么樣哪個杭州seo好
目錄
- 構(gòu)造函數(shù)
- 構(gòu)造函數(shù)體賦值
- 初始化列表
- 初始化列表格式
- 初始化列表的意義以及注意點
- const修飾的成員變量初始化
- 對象成員具體初始化的地方
- 缺省值存在的意義
- 例子1
- 例子2
- 初始化與賦值
- 引用成員變量的初始化
- 注意點1
- 注意點2
- 我的疑惑
- 自定義類型成員初始化
- 例子1
- 例子2
- 例子3
- 例子4
- 初始化列表可以調(diào)用函數(shù)
- 例子1
- 例子2
- 例子3
- 我的疑惑
- 拓展
- 例子1
- 例子2
- 不同類型的賦值
- 編譯器優(yōu)化拷貝構(gòu)造函數(shù)的情景
- 例子1
- 例子2
- 總結(jié)
- explicit關(guān)鍵字
- 例子1
- 例子2
- 補充
感謝各位大佬對我的支持,如果我的文章對你有用,歡迎點擊以下鏈接
🐒🐒🐒 個人主頁
🥸🥸🥸 C語言
🐿?🐿?🐿? C語言例題
🐣🐣🐣 python
🐓🐓🐓 數(shù)據(jù)結(jié)構(gòu)C語言
🐔🐔🐔 C++
🐿?🐿?🐿? 文章鏈接目錄
構(gòu)造函數(shù)
構(gòu)造函數(shù)體賦值
在創(chuàng)建對象時,編譯器通過調(diào)用構(gòu)造函數(shù),給對象中各個成員變量一個合適的初始值
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
雖然上述構(gòu)造函數(shù)調(diào)用之后,對象中已經(jīng)有了一個初始值,但是不能將其稱為對對象中成員變量
的初始化,構(gòu)造函數(shù)體中的語句只能將其稱為賦初值,而不能稱作初始化。因為初始化只能初始
化一次,而構(gòu)造函數(shù)體內(nèi)可以多次賦值。
初始化列表
初始化列表格式
初始化列表:以一個冒號開始,接著是一個以逗號分隔的數(shù)據(jù)成員列表,每個"成員變量"后面跟
一個放在括號中的初始值或表達式。
class Date
{
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}/* Date(int year, int month, int day)之前的構(gòu)造函數(shù)格式{_year = year;_day = day;_month = month;}*/private:int _year;int _month;int _day;
};
下面的寫法都是錯誤的
初始化列表的意義以及注意點
const修飾的成員變量初始化
class Date
{
public:Date(int year, int month, int day){_n = 1;_year = year;_day = day;_month=month;}private:int _year;int _month;int _day;const int _n;
};
int main()
{Date d1(2024, 5, 14);return 0;
}
const int _n不能夠初始化
對于下面的代碼我們都知道,這是聲明,當(dāng)對象實例化的時候他們才會整體定義,比如Date d1(2024,5,14)
private:int _year;int _month;int _day;const int _n;
但是有些成員在定義的時候是必須初始化的,就如 const int _n,因為const修飾了int_n,const只有一次修改的機會就是在初始化的時候,初始化的時候沒有被修改,就會導(dǎo)致后面要想再修改就不可能了,因為const不允許修改被修飾的變量
對象成員具體初始化的地方
那對象成員具體初始化的地方都在哪呢?
上圖我們可以認為是具體的初始化嗎?來看看下面的圖片
定義和初始化應(yīng)該只有一個位置,但是上圖中我們對_year進行了兩次初始化,那這是開辟了兩次空間嗎?
當(dāng)然不是,這樣的情況我們并不想讓他發(fā)生
所以構(gòu)造函數(shù)才有了初始化列表,初始化列表是每個成員變量定義初始化的位置,也就是說想_year=1這樣重復(fù)初始化的就別出現(xiàn)在初始化列表當(dāng)中了
上圖中_month和_day默認初始化為0
在之前的構(gòu)造函數(shù)當(dāng)中,有提到過缺省值
缺省值存在的意義
private:int _year=2;int _month=1;int _day=3;const int _n=1;
這里的_year _month _day _n的缺省值分別為2 1 3 1,而這個缺省值其實就是為初始化列表準(zhǔn)備的,當(dāng)初始化列表當(dāng)中什么都沒有的時候,缺省值就發(fā)揮作用了
例子1
class Date
{
public:Date(int year, int month, int day){}void Print(){cout << _year << "/" << _month << "/" << _day << "/" << _n << endl;}
private:int _year=2;int _month=1;int _day=3;const int _n=1;
};
int main()
{Date d1(2024, 5, 14);d1.Print();return 0;
}
例子2
_year輸出的結(jié)果是2因為在初始化列表當(dāng)中我們對_year初始化成了2,所以并沒有用到缺省值
_month輸出結(jié)果是一個隨機值因為_month即沒給缺省值,又沒對其進行初始化
_day輸出結(jié)果是0,雖然_day給了缺省值,但是在初始化列表當(dāng)中沒有具體給初始化值,所以_day最后初始化的值為0(具體為什么我也不清楚,可能就覺得既然你都有缺省值了,要想讓_day=缺省值,就不要在初始化列表里面寫_day(),這樣讓人感覺你就像讓_day默認初始化成0)
_n輸出結(jié)果是4因為給的缺省值是4,雖然在初始化列表當(dāng)中沒有寫n,但是初始化列表會用這個缺省值給n進行初始化
初始化與賦值
知道了初始化列表的用處后我們看看下面這個代碼
class Date
{
public:Date(int year, int month, int day):_year(1),_month(2), _day(3),_n(4){_year = year;_month = month;_day = day;_year = 1;}void Print(){cout << _year << "/" << _month << "/" << _day << "/" << _n << endl;}
private:int _year=1;int _month;int _day=2;const int _n=4;
};
int main()
{Date d1(2024, 5, 14);d1.Print();return 0;
}
結(jié)果是1/5/14/4,為什么不是1/2/3/4呢?不是說初始化列表值允許初始化1次嗎?
初始化的確只能初始化1次,但是賦值可以賦值很多次
所以大括號里面的_year=year…其實是對_year…賦值
并且從這個例子我們也可以看出誰才是初始化,因為按照程序運行的順序,初始化必然是排在最前面的,賦值是在初始化成功的基礎(chǔ)上才能進行
盡量使用初始化列表初始化,因為不管你是否使用初始化列表,對于自定義類型成員變量,一定會先使用初始化列表初始化,所以最終的寫法如下
class Date
{
public:Date(int year, int month, int day):_year(year),_month(month), _day(day),_n(4){}void Print(){cout << _year << "/" << _month << "/" << _day << "/" << _n << endl;}
private:int _year=1;int _month;int _day=2;const int _n=4;
};
int main()
{Date d1(2024, 5, 14);d1.Print();return 0;
}
注意:
1. 每個成員變量在初始化列表中只能出現(xiàn)一次(初始化只能初始化一次)
2. 類中包含以下成員,必須放在初始化列表位置進行初始化:
引用成員變量
const成員變量
自定義類型成員(且該類沒有默認構(gòu)造函數(shù)時)
引用成員變量的初始化
為什么引用也要房子初始化列表當(dāng)中呢?
在寫引用的文章當(dāng)中說過引用必須要在定義的時候初始化
注意點1
因為是引用,所以在初始化的時候我們可以填入成員變量,如下圖_ref是_year的別名
注意點2
但同時也需要注意因為_ref是引用,所以在初始化的時候需要注意不可以成為const修飾成員的別名
我的疑惑
在寫到這里的時候我還在想像這種引用放在初始化列表開始的位置,在_year還沒初始化的時候,就讓_ref作為_year的別名會不會報錯呢?
這里我說一下我自己的想法,因為_year已經(jīng)聲明了,而_ref作為_year的別名,由于_year沒有初始化,所以_ref只是套了一個空殼子,單號_year初始化后,_ref才真正的初始化成功
為了驗證想法我見year的缺省值刪掉,并且不在初始化列表當(dāng)中加入_year,最后輸出的是隨機值
這時我有一點疑惑,如果沒有初始化_year那_year應(yīng)該只有一個聲明才對,只有一個聲明就說明_year沒有空間,輸出的時候應(yīng)該會報錯才對
于是我打印了一下_year和_ref的地址,發(fā)現(xiàn)他們是有地址的,說明_year和_ref是存在的
為了解釋這種情況我覺得可能是在定義這個對象的時候想_year這些成員變量就已經(jīng)有空間了,但是又和前面所的知識沖突,也可能是我自己沒學(xué)懂😕😕😕
自定義類型成員初始化
例子1
class A
{
public:A(int a = 0):_a(a){cout << "A(int a=0)" << endl;}
private:int _a;
};
class Date
{
public:Date(int year, int month, int day):_ref(_year), _month(month), _day(day), _n(4) {}void Print(){cout << &_year << "/" << _month << "/" << _day << "/" << _n << "/" << &_ref << endl;}
private:int _year;int _month;int _day=2;const int _n=4;int& _ref;A _aa;
};
int main()
{Date d1(2024, 5, 14);d1.Print();return 0;
}
現(xiàn)在有一個自定義類型A,將A_aa的聲明放在Date的成員聲明里,但是A_aa沒有在Date的初始化列表當(dāng)中定義,那A_aa會不會被定義呢?
我們需要理解下面這句話
我們不寫默認構(gòu)造函數(shù),編譯器會自動生成,編譯器自動生成的默認構(gòu)造函數(shù)不對內(nèi)置類型進行處理,自定義類型調(diào)用他自己的默認構(gòu)造函數(shù)
在Date的初始化列表當(dāng)中由于沒有寫A的構(gòu)造函數(shù),所以在運行的過程當(dāng)中會調(diào)用A的默認構(gòu)造函數(shù),而A的默認構(gòu)造函數(shù)是在A這個類里面
例子2
當(dāng)我們不給a默認構(gòu)造呢(只寫了int a,沒有些int a=0)
因為我們給_a初始化成a,但是a沒有給值,所以會報錯
例子3
而當(dāng)我們不給_a初始化成a時,也就是_a(),這樣a就是初始化成0
例子4
我們也可以這樣寫
初始化列表可以調(diào)用函數(shù)
例子1
class Date
{
public:Date():_p((int*)malloc(sizeof(4))){//函數(shù)體if (_p == nullptr){perror("malloc fail");}}void Print(){cout << _p << endl;}
private:int* _p;
};
int main()
{Date d1;d1.Print();return 0;
}
例子2
class Date
{
public:Date(int year, int month, int day):_ref(_year), _month(month), _day(day), _n(4),_p((int*)malloc(sizeof(4))){//函數(shù)體if (_p == nullptr){perror("malloc fail");}}void Print(){cout << &_year << "/" << _month << "/" << _day << "/" << _n << "/" << &_ref << endl;cout << _p << endl;}
private:int _year;int _month;int _day = 2;const int _n = 4;int& _ref;int* _p;
};
int main()
{Date d1(2024, 5, 14);d1.Print();return 0;
}
例子3
class A
{
public:void Print(){cout << a << " " << p1 << " " << *p2 << endl;}
private:int a = 1;int* p1 = nullptr;int* p2 = (int*)malloc(4);};
int main()
{A a;a.Print();
}
因為缺省值是給初始化列表的,這里的int* p2 = (int*)malloc(4)和上面初始化列表當(dāng)中的 ,_p((int*)malloc(sizeof(4)))寫法是相同的,既然初始化列表可以這樣寫,那缺省值也是可以這樣寫的
所以缺省值不一定是常量
我的疑惑
class Date
{
public:Date():_p((int*)malloc(sizeof(4))){//函數(shù)體if (_p == nullptr){perror("malloc fail");}}void Print(){cout << _p << endl;}
private:int* _p;
};
int main()
{Date d1();d1.Print();return 0;
}
這段代碼和上面的例子1的唯一區(qū)別就是Date d1有無括號,但是就是因為這個括號導(dǎo)致報錯了,我也不清楚為什么會這樣
例子2中Date d1有括號,并且也定義了_p,為什么就可以正常運行
除了上面的一些問題還有關(guān)于_p初始化的一些問題等
拓展
例子1
class C
{
public:C(int x = 0):_x(x){}void Print(){cout << _x << endl;}
private:int _x;
};
int main()
{C c1(1);c1.Print();
}
例子2
class C
{
public:C(int x = 0):_x(x){}void Print(){cout << _x << endl;}
private:int _x;
};
int main()
{C c2 = 2;c2.Print();
}
單參數(shù)構(gòu)造函數(shù)支持隱式類型的轉(zhuǎn)換,這里的2構(gòu)造出了一個C的對象,我們先稱為c3,然后將c3拷貝構(gòu)造給c2,這樣的話C c2=2就可以理解成c2=c3,
不同類型的賦值
另外之前提到過不同類型賦值過程也和這個是一樣的
比如現(xiàn)在有一個int類型的a和double類型的b
現(xiàn)在要將b用來給a賦值,在賦值的過程中b會創(chuàng)建出一個臨時變量,然后a拷貝這個臨時變量,因為double類型有8個字節(jié),而int類型只有4個字節(jié),所以拷貝的時候就出現(xiàn)了數(shù)據(jù)丟失
同樣的當(dāng)一個char類型的變量賦值給int類型的變量,因為char類型只有1個字節(jié),而int類型有4個字節(jié),所以當(dāng)char類型賦值給int類型的時候會出現(xiàn)類型提升
所以現(xiàn)在倒回來理解C c2=2應(yīng)該就容易一點了
編譯器優(yōu)化拷貝構(gòu)造函數(shù)的情景
例子1
我們也可以驗證一下他時候調(diào)用了拷貝構(gòu)造函數(shù)
這里沒有調(diào)用拷貝構(gòu)造函數(shù)是因為編譯器優(yōu)化了,同一個表達式連續(xù)步驟的構(gòu)造,一般會合二為一
例子2
class C
{
public:C(int x = 0):_x(x){}C(const C& cc){cout << "C(const C& cc)" << endl;}void Print()const{cout << _x << endl;}
private:int _x;
};
int main()
{const C& c2 = 2;c2.Print();
}
這里的c2引用的是臨時變量,而臨時變量具有常性
總結(jié)
類中以下成員必須放在初始化列表中初始化
引用成員變量
const修飾成員變量
自定義類型成員(且該類沒有構(gòu)造默認成員函數(shù)時)
其他成員可以在函數(shù)體內(nèi)初始化也可以在初始化列表里初始化
這里的函數(shù)體具體是指哪里呢?
Date(int year, int month, int day):_ref(_year), _month(month), _day(day), _n(4) ,_aa(1){//函數(shù)體}
explicit關(guān)鍵字
對于上面的代碼有許多情況都是通過隱式類型轉(zhuǎn)換,如果我們不想讓這種轉(zhuǎn)換發(fā)生,我們可以通過explicit去修飾
例子1
class C
{
public:C(int x = 0):_x(x){}C(const C& cc){cout << "C(const C& cc)" << endl;}void Print()const{cout << _x << endl;}
private:int _x;
};
int main()
{const C& c2 = 2;c2.Print();
}
例子2
多參數(shù)的類也是可以支持explicit修飾
class A
{
public:A(int a1,int a2):_a1(a1),_a2(a2){}
private:int _a1;int _a2;
};
int main()
{A b1 = { 1,2 };const A& b2 = { 1,2 };A b3(1,2);return 0;
}
需要注意的是A b1 = { 1,2 }中用的是花括號
在構(gòu)造時我們我們可以寫成A b3(1,2)
當(dāng)加上explicit之后
class A
{
public:explicit A(int a1,int a2):_a1(a1),_a2(a2){}
private:int _a1;int _a2;
};
int main()
{A b1 = { 1,2 };const A& b2 = { 1,2 };A b3(1,2);return 0;
}
補充
看一下下面這道題
class A
{
public:A(int a):_a1(a),_a2(_a1){}void Print() {cout<<_a1<<" "<<_a2<<endl;}
private:int _a2;int _a1;
};
int main() {A aa(1);aa.Print();
}
A. 輸出1 1
B.程序崩潰
C.編譯不通過
D.輸出1 隨機值
_a2輸出的是一個隨機值,這是因為成員變量在類中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后次序無關(guān)
注意看private的中的_a1和_a2的先后順序,_a2是在_a1前面的,所以_a2要比_a1先賦值
當(dāng)我們調(diào)整順序后,輸出就是1 1了