某日二師兄參加XXX科技公司的C++工程師開發崗位第29面:
面試官:什麼是建構函式?
二師兄:建構函式是一種特殊的成員函數,用於建立和初始化類的物件。建構函式的名稱與類的名稱相同,並且沒有返回型別。建構函式在物件被建立時自動呼叫。
struct Foo
{
Foo(int v):val(i){} //建構函式
private:
int val;
};
面試官:什麼是預設建構函式?什麼情況下預設建構函式會被建立?
二師兄:沒有任何引數的建構函式(所有引數都要預設引數的建構函式也是)。一般定義類時沒有顯式的宣告任何建構函式,預設建構函式會被編譯器自動建立。
struct Foo
{
private:
int val;
}; //此時預設建構函式會被建立
二師兄:當然就算為類自定義了建構函式,我們也可以通過
Foo()=default
為類顯式定義一個預設建構函式。面試官:什麼是建構函式初始值列表?
二師兄:是為了初始化成員變數所傳入的參數列:
class Foo
{
public:
Foo(int i, long l):ival_(i),lval_(l){} //初始值列表
private:
int ival_;
long lval_;
};
面試官:上面的建構函式和以下的建構函式有什麼區別?
Foo(int i, long l)
{
ival_ = i;
lval_ = l;
}
二師兄:這是初始化與賦值的區別。這段程式碼中的
ival_
和lval_
先被預設初始化,然後被賦值。而初始化列表是直接初始化,少了一步賦值。面試官:如果把建構函式寫成
Foo(int i, long l):lval(l),ival_(i){}
會有什麼問題嗎?二師兄:成員初始化的順序儘量要和定義的順序保持一致。如下面的程式碼,就是未定義的:
class Foo
{
public:
Foo(int i):jval_(i),ival_(jval_){} //未定義的行為,因為ival先被初始化,這時候jval是未定義的
private:
int ival_;
int jval_;
};
面試官:什麼是委託建構函式?
二師兄:建構函式在構造物件的時候把一部分任務委託給其他建構函式進行構造,這是C++11引入的新特性:
class Foo
{
public:
Foo(int i, long l):ival_(i),lval_(l){}
Foo(int i):Foo(i,0){} //委託給Foo(int i, long l)
private:
int ival_;
long lval_;
};
面試官:如果建構函式沒有初始化任何成員變數,使用這個建構函式會發生什麼?
二師兄:成員變數將會被預設初始化。
面試官:什麼是預設初始化?
二師兄:如果是內建型別(如
bool
、int
、double
),將不被初始化,如果是類型別,將執行類型別的的預設建構函式初始化變數。如果類型別的預設建構函式是刪除的(=delete
)或定義了其他建構函式但是沒有定義預設建構函式的,將不能通過編譯。二師兄:類型別的初始化時一個迴圈的過程,如果類型別中有類型別成員,初始化方式和以上描述的一致。
struct Foo{ int a;}
struct Goo
{
int b;
Foo f;
};
Goo g; //此g.b是預設初始化,值不確定。Foo中的a也是預設初始化,所以g.f.a的值也是不確定的。
面試官:可以使用
virtual
修飾建構函式嗎?二師兄:不可以,因為建構函式在物件構造階段呼叫,虛表尚未建立,所以無法呼叫虛擬函式實現多型。
面試官:可以使用
const
修飾建構函式嗎?二師兄:不可以,因為建構函式需要初始化成員變數,這與
const
修飾成員函數的意義相悖。面試官:可以使用
constexpr
修飾建構函式嗎?二師兄:可以。這表明類的物件可以在編譯器構造。我們所熟悉的
std::array
的建構函式在C++20下就是constexpr
的。面試官:什麼情況下會將一個類別建構函式定義為私有的?
二師兄:一般不希望直接通過型別定義物件,如C++的單例模式:
class Singleton
{
public:
static Singleton& Instance()
{
static Singleton instance;
return instance;
}
Singleton(const Singleton&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton& operator=(Singleton&&) = delete;
private:
Singleton() = default;
~Singleton() = default;
};
Singleton s; //編譯失敗
Singleton& s = Singleton::Instance(); //編譯成功
面試官:最後一個問題,你知宣告、定義、初始化、賦值的區別嗎?
二師兄:宣告是告訴編譯器這裡有個符號,但不分配記憶體。定義告訴編譯器,這裡有個符號,要分配一塊記憶體給它。初始化時在分配記憶體的時候給它一個初始值。賦值是將這塊記憶體原來的值擦除,給它填入一個新值。
面試官:好的,今天的面試結束了,請回去等通知吧。
C++類別的建構函式的基本考點都在這裡了,小夥伴本要理解這些設計及設計背後的取捨,面對面試官的拷問才能對答如流哦。
好了,今天的面試到這裡就結束了,讓我們期待明天面試官和二師兄的表現吧~
關注我,帶你21天「精通」C++!(狗頭)