Qt C 建構函式與explicit

2021-09-08 19:03:02 字數 4406 閱讀 5213

1、預設建構函式

預設建構函式是指所有引數都提供了預設值的建構函式,通常指無參的建構函式或提供預設值的建構函式。如類test1和test2的建構函式

class

test1 //

default constructor

} ;或

class

test2

//default constructor

} ;

如果你沒有為你的類提供任何建構函式,那麼編譯器將自動為你生成乙個預設的無參建構函式。一旦你為你的類定義了建構函式,哪怕只是乙個,那麼編譯器將不再生成預設的建構函式。

2、何時需要為你的類提供預設建構函式

有很多情況,列舉如下:

1. 當你使用靜態分配的陣列,而陣列元素型別是某個類的物件時,就要呼叫預設的建構函式,比如下面的**。

object buffer[10]; //

call default constructor

2. 當你使用動態分配的陣列,而陣列元素型別是某個類的物件時,就要呼叫預設的建構函式,比如下面的**,如果object沒有預設的建構函式,是無法通過編譯的,因為new操作符要呼叫object類的無參建構函式類初始化每個陣列元素

object* buffer = new object[10];

3. 當你使用標準庫的容器時,如果容器內的元素型別是某個類的物件時,那麼這個類就需要預設的建構函式,原因同上。

vectorbuffer;

4.乙個類a以另外某個類b的物件為成員時,如果a提供了無參建構函式,而b未提供,那麼a則無法使用自己的無參建構函式。下面的**將導致編譯錯誤。

classb};

class

a b b;

};int main(void

)

再比如下面的**,類a定義了拷貝建構函式,而沒有提供預設的建構函式,b繼承自a,所以b在初始化時要呼叫a的建構函式來初始化a,而a沒有預設的建構函式,故產生編譯錯誤。

classa};

class b : publica;

int main(void

)

除以上情況之外還有很多,不在此一一舉例描述。

當我們新建了乙個qt的widgets應用工程時。會自動生成乙個框架,包含了幾個檔案。其中有個mainwindow.h的標頭檔案。就是你要操縱的ui主介面了。我們看看其中的一段**:

class mainwindow : public

qmainwindow

;

這段**定義了乙個新的類mainwindow,繼承自qmainwindow。我們可以看到在它的建構函式裡,前面有乙個關鍵字 explicit 。相信大家都對沒有這個關鍵字的建構函式不陌生。那麼這個 explicit 是起到什麼作用的呢?

explicit是c++中的關鍵字,不是c語言中的。英文直譯是「明確的」、「顯式的」意思。出現這個關鍵字的原因,是在c++中有這樣規定的基礎上:當定義了只有乙個引數的建構函式時,同時也定義了一種隱式的型別轉換。先看型別轉換。

c/c++中,有很多態別轉換。比如:

double a = 12.34

;int b = (int)a;

我們都知道這時b的值是12. 在變數前面加括號包裹的型別,就能實現顯式的型別轉換。這種叫做強制型別轉換。順便值得一提的是,c++中還支援這種強制型別轉換的例子:

double a = 12.34

;int b = int(a);

除此之外,還有一種轉換叫做 隱式型別轉換。

double a = 12.34

;int b = a;

同樣的,b的值也是12.雖然沒有顯式的轉換型別,但是編譯器會幫你自動轉換。同樣的,不僅是基本資料型別,自己定義的類和物件之間也存在這種轉換關係。

比如你有乙個類的物件a:

classa  

intgetvalue()

;private:

inta;

};

你會發現,你在main函式中,使用下面的語句時是合法的:

a a = 10;

之所以類a的物件可以直接使用整型通過等於號來初始化,是因為這一語句呼叫了預設的單引數建構函式,其效果等價於a temp(10); a(temp);

首先編譯器執行a temp(10);在棧中建立了乙個臨時物件(假設叫做temp)。然後再呼叫物件a的拷貝初始化建構函式 a(temp) 給a初始化。然後臨時物件temp銷毀。這就是編譯器做的隱式轉換工作。你可以想到這樣的隱式操作的結果和直接顯示呼叫a a(10);的結果是一樣的,但是隱式轉換因為使用了拷貝建構函式所以在開銷上會更高一些。當然這基本資料型別,或許不明顯。如果乙個複雜的物件,比如qt的視窗。那麼開銷可想而知。

又如當你使用如下語句會不通過:

a a = "

123";

因為沒有引數為字串的單引數建構函式。知道了這個,你修改一下就能通過了。

class

a a(

char *c)

intgetvalue()

;private:

inta;

};

我們再定義乙個函式print 用來列印a物件的值。

void

print(a a)

;

在main函式中:

void

main()

這樣是可以編譯執行的。雖然我們並沒有建立乙個類a的物件來傳給print 函式。但是編譯器缺省會呼叫類a的單引數建構函式,建立出乙個類a的物件出來。 

上面可以看出編譯器會為你做一些,隱式的型別轉換工作。這或許會讓你感到方便,但是有時候卻會帶來麻煩。我們來做乙個假設:

上面這個類a,只接受整型和字串型。所以你想傳遞乙個字串 「2」 作為引數來構造乙個新的物件。比如 a b = 「2」; 然而,你卻寫錯了,雙引號寫成了單引號變成了 a b = 『2』; 然而這樣並不會報錯。編譯器 會把 字元 『2』 轉型成 整型 也就是它的ascll碼—— 50。為了避免這樣我們不希望的隱式轉換,我們可以加上explicit 關鍵字。

public:  

explicit a(int

i)

這樣就能避免隱式的型別轉換了,當你誤寫成單引號的時候,就會報錯。這樣就只允許顯示的呼叫單引數建構函式了。如 a a(10); a b("123"); 

不僅如此,在加上explicit之後,print函式也會報錯了。因為編譯器不會主動呼叫explicit標識的構造器。這就需要你自己顯示的來呼叫了:

print(a(10));

當然了,是否應該禁止隱式轉換是沒有定論的,沒有一種放之四海皆準的標準,具體看你的情景需要了。 

一般而言,顯示呼叫構造器,能避免一些麻煩,讓程式設計師手動來管理。很多人說c++難,因為很多東西對於程式設計師來說不是透明的,比如記憶體釋放什麼的,這個顯式呼叫也是需要程式設計師自己動手的。然而我感覺這正是c++的魅力所在,c++給了程式設計師幾乎等同於上帝的權力,所有一切都能自己掌控,還比如運算子過載的權力,甚至像qt這樣可以自定義slot和signal關鍵字(其實是巨集),這在其他高階語言裡是不可想象的。當然了,語言這東西,是仁者見仁智者見智的。沒必要爭論優劣。我一直認為的是:沒有最優秀的語言,只有最合適的語言。程式語言本身沒有優劣之分,但是不同程式設計師對於不同語言確有好噁之別。 

explicit使用注意事項:

* explicit 關鍵字只能用於類內部的建構函式宣告上。

* explicit 關鍵字作用於單個引數的建構函式(或者除了第乙個引數外其餘引數都有預設值的多參建構函式),如circle(int x, int y = 0) 。

* 在c++中,explicit關鍵字用來修飾類的建構函式,被修飾的建構函式的類,不能發生相應的隱式型別轉換,只能以顯示的方式進行型別轉換。

explicit關鍵字只用在類內部的宣告中。在外部的實現部分不需要使用。

#includeusing

namespace

std;

classa

intgetvalue()

;private:

inta;

};a::a(

int i)//

無需再指明explicit

void

print(a a)

;void

main()

**:

函式與建構函式

建立乙個物件的方式中有乙個建構函式模式。ecmascript中的建構函式是用於建立特定型別物件的。如object和array這樣的原生建構函式,執行時可以直接在執行環境中使用。還可以自定義建構函式,以函式的形式為自己的物件型別定義屬性和方法 如乙個建構函式 function student name...

建構函式與this

class person person string name,int age public void setage int age public intgetage void show 在示例 中,person 即為person類的建構函式。當定義乙個類的時候,即使沒有顯式的寫出建構函式,程式也會...

拷貝建構函式與賦值建構函式

include stdafx.h include include using namespace std class a a a a 過載拷貝函式 a int id,char t name a char name a operator a a 注意 此處一定要返回物件的引用,否則返回後其值立即消失!...