某日二師兄參加XXX科技公司的C++工程師開發崗位第29面: > 面試官:什麼是構造函數? > > 二師兄:構造函數是一種特殊的成員函數,用於創建和初始化類的對象。構造函數的名稱與類的名稱相同,並且沒有返回類型。構造函數在對象被創建時自動調用。 ```c++ struct Foo { Foo(int ...
某日二師兄參加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++!(狗頭)