在 C++ 中, 如果一個類有隻有一個參數的構造函數,C++ 允許一種特殊的聲明類變量的方式。在這種情況下,可以直接将一個對應于構造函數參數類型的資料直接指派給類變量,編譯器在編譯時會自動進行類型轉換,将對應于構造函數參數類型的資料轉換為類的對象。 如果在構造函數前加上 explicit 修飾詞,
則會禁止這種自動轉換,在這種情況下, 即使将對應于構造函數參數類型的資料直接指派給類變量,編譯器也會報錯。
下面以具體執行個體來說明。
建立people.cpp 檔案,然後輸入下列内容:
/*人類,是人的類,不是人類 -:) */
class People
{
public:
int age;
explicit People (int a)
age=a;
}
};
/*三種方式來 “造人” */
void foo ( void )
People p1(10); //方式一
People* p_p2=new People(10); //方式二
People p3=10; //方式三
這段 C++ 程式定義了一個類 people ,包含一個構造函數, 這個構造函數隻包含一個整形參數 a ,可用于在構造類時初始化 age 變量。
然後定義了一個函數foo,在這個函數中我們用三種方式分别建立了三個10歲的“人”。 第一種是最一般的類變量聲明方式。第二種方式其實是聲明了一個people類的指針變量,然後在堆中動态建立了一個people執行個體,并把這個執行個體的位址指派給了p_p2。第三種方式就是我們所說的特殊方式,為什麼說特殊呢?
我們都知道,C/C++是一種強類型語言,不同的資料類型是不能随意轉換的,如果要進行類型轉換,必須進行顯式強制類型轉換,而這裡,沒有進行任何顯式的轉換,直接将一個整型資料指派給了類變量p3。
是以,可以說,這裡進行了一次隐式類型轉換,編譯器自動将對應于構造函數參數類型的資料轉換為了該類的對象,是以方式三經編譯器自動轉換後和方式一最終的實作方式是一樣的。
不相信? 耳聽為虛,眼見為實,讓我們看看底層的實作方式。
為了更容易比較方式一和方式三的實作方式,我們對上面的代碼作一點修改,去除方式二:
去除方式二的原因是方式二是在堆上動态建立類執行個體,是以會有一些額外代碼影響分析。修改完成後,用下列指令編譯 people.cpp
$ gcc -S people.cpp
"-S"選項是GCC輸出彙編代碼。指令執行後,預設生成people.s。 關鍵部分内容如下:
.globl _Z3foov
.type _Z3foov, @function
_Z3foov:
.LFB5:
pushl %ebp
.LCFI2:
movl %esp, %ebp
.LCFI3:
subl $24, %esp
.LCFI4:
movl $10, 4(%esp)
leal -4(%ebp), %eax
movl %eax, (%esp)
call _ZN6PeopleC1Ei
leal -8(%ebp), %eax
leave
ret
看“.LCFI4” 行後面的東西,1-4行和5-8行幾乎一模一樣,1-4行即為方式一的彙編代碼,5-8即為方式三的彙編代碼。 細心的你可能發現2和6行有所不同,一個是 -4(%ebp) 而另一個一個是 -8(%ebp) ,這分别為類變量P1和P3的位址。
對于不可随意進行類型轉換的強類型語言C/C++來說, 這可以說是C++的一個特性。哦,今天好像不是要說C++的特性,而是要知道explicit關鍵字的作用?
explicit關鍵字到底是什麼作用呢? 它的作用就是禁止這個特性。如文章一開始而言,凡是用explicit關鍵字修飾的構造函數,編譯時就不會進行自動轉換,而會報錯。
讓我們看看吧! 修改代碼:
然後再編譯:
編譯器立馬報錯:
people.cpp: In function ‘void foo()’:
people.cpp:23: 錯誤:請求從 ‘int’ 轉換到非标量類型 ‘People’