專業(yè)的設(shè)計(jì)網(wǎng)站有哪些中國(guó)站免費(fèi)推廣入口
目錄
一、初始化列表?
1、定義?
2、注意事項(xiàng)
3、盡量使用初始化列表初始化
4、初始化順序
二、?explicit關(guān)鍵字
1、定義
2、特點(diǎn)
三、static成員
1、定義?
2、特性?
3、例題
一、初始化列表?
下面這段代碼可以正常編譯:
class A {
private:int _a1;//成員聲明int _a2;
};
int main()
{A a;//對(duì)象整體定義return 0;
}
如果加上一個(gè)const類型的成員變量_x,編譯就無法通過。?
class A {
private:int _a1;int _a2;const int _x;
};
int main()
{A a;return 0;
}
這是因?yàn)閏onst變量必須在定義的位置初始化,否則編譯不通過。
class A {
private:int _a1;//聲明int _a2;const int _x;
};
在private作用域中,const變量和兩個(gè)int變量都是成員變量的聲明,如果我們聲明const變量,一定要對(duì)它進(jìn)行定義,那我們?cè)谀亩x呢?
C++11之后可以在聲明位置為變量賦初值。
const int _x = 0;
那在C++11之前,也有解決方法,給每個(gè)成員變量找一個(gè)位置對(duì)其進(jìn)行定義,這樣就解決了變量初始化的問題,這個(gè)位置使用初始化列表進(jìn)行初始化賦值。
1、定義?
初始化列表:以一個(gè)冒號(hào)開始,接著是一個(gè)以逗號(hào)分隔的數(shù)據(jù)成員列表,每個(gè)"成員變量"后面跟一個(gè)放在括號(hào)中的初始值或表達(dá)式。
class A {
public:A():_x(1){}
private:int _a1;int _a2;const int _x;
};
int main()
{A a;return 0;
}
只要對(duì)象調(diào)用構(gòu)造函數(shù),初始化列表是它所有成員變量定義的位置。
不管是否顯示在初始化列表寫,那么編譯器每個(gè)變量都會(huì)初始化列表定義初始化。
class A {
public:A():_x(1),_a1(6){}
private:int _a1 = 1;int _a2 = 2;const int _x;
};
int main()
{A a;return 0;
}
在初始化列表中初始化的變量,不使用缺省值;沒有使用初始化列表的變量,使用缺省值。?
2、注意事項(xiàng)
- 每個(gè)成員變量在初始化列表中只能出現(xiàn)一次(初始化只能初始化一次)
- 類中包含以下成員,必須放在初始化列表位置進(jìn)行初始化:
- 引用成員變量
- const成員變量
- 自定義類型成員(且該類沒有默認(rèn)構(gòu)造函數(shù)時(shí))
class B {
public:B():_b(0){cout << "B()" << endl;}
private:int _b;
};class A {
private:B _bb;
};
int main()
{A aa;return 0;
}
?這里的aa的成員變量自定義類型_bb是可以調(diào)用它的默認(rèn)構(gòu)造函數(shù)的初始化列表進(jìn)行初始化。
?默認(rèn)構(gòu)造可以是無參或全缺省的。
class B {
public:B(int n) :_b(0)//會(huì)報(bào)錯(cuò)B(int n=9) :_b(0)//全缺省B( ) :_b(0)//無參
private:int _b;
};
3、盡量使用初始化列表初始化
?下面看一個(gè)用兩個(gè)棧實(shí)現(xiàn)的隊(duì)列。
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){cout << "Stack(size_t capacity = 10)" << endl;_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申請(qǐng)空間失敗");exit(-1);}_size = 0;_capacity = capacity;}void Push(const DataType& data){_array[_size] = data;_size++;}Stack(const Stack& st){cout << "Stack(const Stack& st)" << endl;_array = (DataType*)malloc(sizeof(DataType)*st._capacity);if (nullptr == _array){perror("malloc申請(qǐng)空間失敗");exit(-1);}memcpy(_array, st._array, sizeof(DataType)*st._size);_size = st._size;_capacity = st._capacity;}~Stack(){cout << "~Stack()" << endl;if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}private:DataType *_array;size_t _size;size_t _capacity;
};class MyQueue
{
public:MyQueue(int pushN, int popN):_pushST(pushN), _popST(popN){}private:Stack _pushST;Stack _popST;int _size = 0;
};int main()
{ MyQueue q(2, 3);return 0;
}
在調(diào)試中可以看到,這里的2
和3
分別作為參數(shù)傳遞給MyQueue
的構(gòu)造函數(shù),通過初始化列表對(duì)這兩個(gè)成員變量進(jìn)行初始化。
?如果我們使用這種無參的構(gòu)造函數(shù)對(duì)MyQueue對(duì)象初始化呢?
class MyQueue
{
public:MyQueue(){}private:Stack _pushST;Stack _popST;int _size = 0;
};
可以看到,如果我們不寫初始化列表,MyQueue類也可以調(diào)用Stack的默認(rèn)構(gòu)造函數(shù)對(duì)兩個(gè)Stack類的對(duì)象進(jìn)行初始化,不寫MyQueue的構(gòu)造函數(shù)也會(huì)使用同樣方式初始化,本質(zhì)上一樣。
4、初始化順序
成員變量在類中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后次序無關(guān)。
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();
}
_a2比_a1先聲明,所以_a2比_a1先在初始化列表中初始化,_a2初始化時(shí)_a1還沒初始化,所以_a2是隨機(jī)值。
二、?explicit關(guān)鍵字
class A {
public:A(int a):_a1(a){}
private:int _a2;int _a1;
};
int main()
{A aa1(1); //構(gòu)造函數(shù)A aa2 = 1; //隱式類型轉(zhuǎn)換int i = 1; double d = i;//隱式類型轉(zhuǎn)換return 0;
}
默認(rèn)情況下,這里的隱式類型轉(zhuǎn)換都會(huì)借助額外創(chuàng)建的臨時(shí)變量實(shí)現(xiàn),通過構(gòu)造創(chuàng)建臨時(shí)變量,然后拷貝構(gòu)造給變量賦值的過程被優(yōu)化為直接構(gòu)造,下一篇文章詳細(xì)講解優(yōu)化過程。
在這兩種情況下,臨時(shí)變量的創(chuàng)建是為了完成類型轉(zhuǎn)換的過程。這些臨時(shí)變量在轉(zhuǎn)換完成后會(huì)被銷毀,對(duì)于程序的其他部分是不可見的。這種臨時(shí)變量的創(chuàng)建和銷毀是由編譯器自動(dòng)處理的,無需手動(dòng)干預(yù)。
-
A aa2 = 1;
?這里發(fā)生了從int到A的隱式類型轉(zhuǎn)換。編譯器會(huì)自動(dòng)調(diào)用A類的構(gòu)造函數(shù)來創(chuàng)建一個(gè)臨時(shí)的A對(duì)象,然后將整數(shù)值1傳遞給構(gòu)造函數(shù)作為參數(shù)。這個(gè)臨時(shí)的A對(duì)象會(huì)被復(fù)制到aa2中,完成隱式類型轉(zhuǎn)換。 -
double d = i;
?這里發(fā)生了從int到double的隱式類型轉(zhuǎn)換。編譯器會(huì)創(chuàng)建一個(gè)臨時(shí)的double變量,并將整數(shù)變量i的值復(fù)制到這個(gè)臨時(shí)變量中。然后,這個(gè)臨時(shí)的double變量的值會(huì)被賦給變量d,完成隱式類型轉(zhuǎn)換。
拷貝構(gòu)造也屬于構(gòu)造,也可以使用初始化列表,但下面的成員變量會(huì)調(diào)用拷貝構(gòu)造嗎?
class A
{
public:A(int a):_a1(a){cout << "A(int a)" << endl;}A(const A& aa):_a1(aa._a1){cout << "A(const A& aa)" << endl;}private:int _a2;int _a1;
};int main()
{A aa1(1); //構(gòu)造函數(shù)A aa2 = 1; //隱式類型轉(zhuǎn)換return 0;
}
輸出結(jié)果發(fā)現(xiàn)沒有調(diào)用引用類型的拷貝構(gòu)造。
?
這是因?yàn)镃++中編譯器會(huì)對(duì)自定義類型的進(jìn)行優(yōu)化, 將構(gòu)造+拷貝+優(yōu)化的過程優(yōu)化成一個(gè)構(gòu)造。
那下面的代碼中,為什么第一個(gè)會(huì)報(bào)錯(cuò),第二個(gè)沒問題呢??
A& ref = 10;
const A& ref = 10;
- 這是因?yàn)樵贑++中,當(dāng)你聲明一個(gè)引用(比如?
A& ref
)并試圖將其初始化為一個(gè)右值(比如一個(gè)臨時(shí)對(duì)象或一個(gè)字面量),編譯器通常會(huì)報(bào)錯(cuò)。這是因?yàn)榉莄onst引用不能綁定到右值上,防止對(duì)臨時(shí)對(duì)象的非常量引用,因?yàn)檫@可能導(dǎo)致對(duì)臨時(shí)對(duì)象的意外修改,從而導(dǎo)致不確定的行為。但是,當(dāng)你聲明一個(gè)常量引用(比如?const A& ref
),編譯器允許這種綁定,因?yàn)槌A恳每梢越壎ǖ接抑瞪稀?/span> - 在上述代碼中,
const A& ref = 10;
?這行代碼中的?10
?是一個(gè)整數(shù)字面量,是一個(gè)右值。你嘗試將這個(gè)右值綁定到引用?ref
?上。由于?ref
?被聲明為?const A&
,它是一個(gè)常量引用,所以編譯器允許這種綁定,并調(diào)用?A
?類的構(gòu)造函數(shù)?A(int a)
?來創(chuàng)建一個(gè)臨時(shí)的?A
?對(duì)象,然后將?ref
?綁定到這個(gè)臨時(shí)對(duì)象上。
1、定義
對(duì)于單參構(gòu)造函數(shù):沒有使用explicit修飾,具有類型轉(zhuǎn)換作用explicit修飾構(gòu)造函數(shù),禁止類型轉(zhuǎn)換---explicit去掉之后,代碼可以通過編譯。
?對(duì)于剛剛的代碼,如果在構(gòu)造函數(shù)前加explicit程序會(huì)怎么樣呢?
class A{
public:explicit A(int a):_a1(a){cout << "A(int a)" << endl;}A(const A& aa):_a1(aa._a1){cout << "A(const A& aa)" << endl;}private:int _a2;int _a1;
};
int main()
{A aa1(1); //構(gòu)造函數(shù)A aa2 = 1; //隱式類型轉(zhuǎn)換const A& ref = 10;return 0;
}
?這兩段代碼會(huì)報(bào)錯(cuò),程序禁止類型轉(zhuǎn)換。
?
2、特點(diǎn)
class A
{
public://explicit A(int a)A(int a):_a1(a){cout << "A(int a)" << endl;}//explicit A(int a1, int a2)A(int a1, int a2):_a1(a1), _a2(a2){}private:int _a2;int _a1;
};
int main()
{// 單參數(shù)構(gòu)造函數(shù) C++98A aa1(1); //構(gòu)造函數(shù)A aa2 = 1; //隱式類型轉(zhuǎn)換// 多參數(shù)構(gòu)造函數(shù) C++11A aa2(1, 1);//A aa3= 2,2;//C98不支持A aa3 = { 2, 2 };//C++11支持return 0;
}
-
A aa1(1);
?這是直接調(diào)用單參數(shù)構(gòu)造函數(shù)創(chuàng)建對(duì)象的例子。 -
A aa2 = 1;
?這是一個(gè)隱式類型轉(zhuǎn)換的例子。這里,整數(shù)1被隱式地轉(zhuǎn)換為類A的一個(gè)對(duì)象。這是因?yàn)轭怉定義了一個(gè)接受int類型參數(shù)的構(gòu)造函數(shù),因此編譯器會(huì)自動(dòng)調(diào)用該構(gòu)造函數(shù)來創(chuàng)建一個(gè)臨時(shí)的A對(duì)象,并將其賦值給aa2。 -
A aa2(1, 1);
?這是直接調(diào)用雙參數(shù)構(gòu)造函數(shù)創(chuàng)建對(duì)象的例子。 -
A aa3 = { 2, 2 };
?這是C++11引入的列表初始化的例子。這種方式可以用來初始化對(duì)象,而不需要顯式地調(diào)用構(gòu)造函數(shù)。
explicit
關(guān)鍵字用于阻止編譯器進(jìn)行不希望發(fā)生的隱式類型轉(zhuǎn)換。如果你將構(gòu)造函數(shù)前面的注釋去掉,使得構(gòu)造函數(shù)前面有explicit
關(guān)鍵字,那么像A aa2 = 1;
這樣的隱式類型轉(zhuǎn)換就會(huì)被禁止,編譯器會(huì)報(bào)錯(cuò)。
例如,如果你將單參數(shù)構(gòu)造函數(shù)改為explicit A(int a)
,那么A aa2 = 1;
這行代碼就會(huì)導(dǎo)致編譯錯(cuò)誤,因?yàn)榫幾g器被禁止進(jìn)行從int到A的隱式類型轉(zhuǎn)換。你必須顯式地調(diào)用構(gòu)造函數(shù),像A aa2(1);
這樣。
總的來說,explicit
關(guān)鍵字可以幫助你控制類型轉(zhuǎn)換,防止因?yàn)椴幌M碾[式類型轉(zhuǎn)換而導(dǎo)致的錯(cuò)誤。
三、static成員
實(shí)現(xiàn)一個(gè)類,計(jì)算程序中創(chuàng)建了多少類對(duì)象
int count = 0;
class A
{
public:A(int a = 0){++count;}A(const A& aa){++count;}
};
void func(A a)
{}
int main()
{A aa1;A aa2(aa1);func(aa1);A aa3 = 1;cout << count << endl;return 0;
}
造成了命名沖突的問題,因?yàn)镃++的xutility文件里有個(gè)函數(shù)count與我們定義的全局變量count沖突了。
我們可以不展開std,只調(diào)用需要用的流輸入輸出即可。
#include <iostream>
//using namespace std;
using std::cout;
using std::endl;
成功輸出:?
C++為了解決上述問題,同時(shí)可以將std展開,將count作為類的static修飾的成員即可實(shí)現(xiàn)。
1、定義?
- 聲明為static的類成員稱為類的靜態(tài)成員,用static修飾的成員變量,稱之為靜態(tài)成員變量;
- 用static修飾的成員函數(shù),稱之為靜態(tài)成員函數(shù)。
- 靜態(tài)成員變量一定要在類外進(jìn)行初始化。
- 靜態(tài)成員不屬于某個(gè)對(duì)象,所于所有對(duì)象,屬于整個(gè)類。
- 靜態(tài)成員變量的初始化通常在類外部進(jìn)行。
class A
{
public:A(int a = 0){++count;}A(const A& aa){++count;}int Getcount(){return count;}
private:static int count; // 此處為聲明int _a = 0;
};int A::count = 0; // 定義初始化void func(A a)
{}
當(dāng)我們想輸出時(shí):
int main()
{A aa1;A aa2(aa1);func(aa1);A aa3 = 1;cout << A::Getcount() << endl;return 0;
}

如果想要輸出,可以使用靜態(tài)成員函數(shù)。
//靜態(tài)成員函數(shù) 沒有this指針static int Getcount(){// _a++; // 不能直接訪問非靜態(tài)成員return count;}
成功輸出:
下面語句創(chuàng)建出了多少個(gè)類對(duì)象?
A aa4[10];
輸出結(jié)果:

2、特性?
- 靜態(tài)成員為所有類對(duì)象所共享,不屬于某個(gè)具體的對(duì)象,存放在靜態(tài)區(qū)
- 靜態(tài)成員變量必須在類外定義,定義時(shí)不添加static關(guān)鍵字,類中只是聲明
- 類靜態(tài)成員即可用 類名::靜態(tài)成員 或者 對(duì)象.靜態(tài)成員 來訪問
- 靜態(tài)成員函數(shù)沒有隱藏的this指針,不能訪問任何非靜態(tài)成員
- 靜態(tài)成員也是類的成員,受public、protected、private 訪問限定符的限制
3、例題
求1+2+3+...+n_??皖}霸_??途W(wǎng) (nowcoder.com)
- 下面這段代碼實(shí)現(xiàn)了一個(gè)類?
Sum
?和一個(gè)類?Solution
,其中?Sum
?類用于計(jì)算從1到n的累加和,而?Solution
?類則使用?Sum
?類來計(jì)算給定整數(shù)n的累加和。?- 這種設(shè)計(jì)利用了類的構(gòu)造函數(shù)和靜態(tài)成員變量的特性,實(shí)現(xiàn)了累加和的計(jì)算和獲取。
class Sum{
public:Sum(){_sum+=_i;_i++;}static int Getsum(){return _sum;}
private:static int _sum;static int _i;
};
int Sum::_sum = 0;
int Sum::_i = 1;
class Solution {
public:int Sum_Solution(int n) {Sum a[n];return Sum::Getsum();}
};
首先,讓我們逐步解釋?Sum
?類的實(shí)現(xiàn):
Sum
?類有兩個(gè)靜態(tài)成員變量?_sum
?和?_i
,分別用于保存累加和和當(dāng)前的計(jì)數(shù)器值。- 構(gòu)造函數(shù)?
Sum()
?是一個(gè)無參構(gòu)造函數(shù),每次被調(diào)用時(shí),它會(huì)將當(dāng)前計(jì)數(shù)器值?_i
?加到累加和?_sum
?中,并將計(jì)數(shù)器?_i
?自增1。 - 靜態(tài)成員函數(shù)?
Getsum()
?用于獲取累加和?_sum
?的值。
接下來,我們來看?Solution
?類的實(shí)現(xiàn):
Solution
?類中的成員函數(shù)?Sum_Solution(int n)
?接受一個(gè)整數(shù)?n
?作為參數(shù),并返回從1到n的累加和。- 在?
Sum_Solution
?函數(shù)中,我們創(chuàng)建了一個(gè)名為?a
?的?Sum
?類型的數(shù)組,數(shù)組的大小為?n
。 - 由于?
Sum
?類的構(gòu)造函數(shù)會(huì)在創(chuàng)建對(duì)象時(shí)自動(dòng)調(diào)用,因此創(chuàng)建數(shù)組?a
?的過程中,會(huì)依次調(diào)用?Sum
?類的構(gòu)造函數(shù),從而實(shí)現(xiàn)了從1到n的累加和的計(jì)算。 - 最后,我們通過調(diào)用?
Sum::Getsum()
?函數(shù)來獲取累加和的值,并將其作為函數(shù)的返回值。