1、默認構造函數
默認構造函數是指所有參數都提供了默認值的構造函數,通常指無參的構造函數或提供默認值的構造函數。如類Test1和Test2的構造函數
class Test1 { public: Test1(){} // default constructor } ; 或 class Test2 { public: Test2(int i=1){} // default constructor } ;
如果你沒有為你的類提供任何構造函數,那么編譯器將自動為你生成一個默認的無參構造函數。一旦你為你的類定義了構造函數,哪怕只是一個,那么編譯器將不再生成默認的構造函數。
2、何時需要為你的類提供默認構造函數
有很多情況,列舉如下:
1. 當你使用靜態分配的數組,而數組元素類型是某個類的對象時,就要調用默認的構造函數,比如下面的代碼。
Object buffer[10]; // call default constructor
2. 當你使用動態分配的數組,而數組元素類型是某個類的對象時,就要調用默認的構造函數,比如下面的代碼,如果Object沒有默認的構造函數,是無法通過編譯的,因為new操作符要調用Object類的無參構造函數類初始化每個數組元素。
Object* buffer = new Object[10];
3. 當你使用標准庫的容器時,如果容器內的元素類型是某個類的對象時,那么這個類就需要默認的構造函數,原因同上。
vector<Object> buffer;
4. 一個類A以另外某個類B的對象為成員時,如果A提供了無參構造函數,而B未提供,那么A則無法使用自己的無參構造函數。下面的代碼將導致編譯錯誤。
class B { B(int i){} }; class A { A(){} B b; }; int main(void) { A a(); // error C2512: 'B' : no appropriate default constructor available getchar() ; return 0 ; }
再比如下面的代碼,類A定義了拷貝構造函數,而沒有提供默認的構造函數,B繼承自A,所以B在初始化時要調用A的構造函數來初始化A,而A沒有默認的構造函數,故產生編譯錯誤。
class A { A(const A&){} }; class B : public A { }; int main(void) { B b; //error C2512:'B': no appropriate default constructor available getchar() ; return 0 ; }
除以上情況之外還有很多,不在此一一舉例描述。
參考自:http://www.cnblogs.com/graphics/archive/2012/10/02/2710340.html
從Qt談到C++(一):關鍵字explicit與隱式類型轉換
提出疑問
當我們新建了一個Qt的widgets應用工程時。會自動生成一個框架,包含了幾個文件。其中有個mainwindow.h的頭文件。就是你要操縱的UI主界面了。我們看看其中的一段代碼:
class MainWindow : public QMainWindow { Q_OBJECT//一個宏,暫不考慮 public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private: Ui::MainWindow *ui; };
這段代碼定義了一個新的類MainWindow,繼承自QMainWindow。我們可以看到在它的構造函數里,前面有一個關鍵字 explicit 。相信大家都對沒有這個關鍵字的構造函數不陌生。那么這個 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:
class A { public: A(int i) { a = i; } int getValue() { return a; }; private: int a; };
你會發現,你在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 { public: A(int i) { a = i; } A(char * c) { a=c[0]; } int getValue() { return a; }; private: int a; };
函數調用
我們再定義一個函數print 用來打印A對象的值。
void print(A a) { cout<<a.getValue(); };
在main函數中:
void main() { print(10); }
這樣是可以編譯運行的。雖然我們並沒有創建一個類A的對象來傳給print 函數。但是編譯器默認會調用類A的單參數構造函數,創建出一個類A的對象出來。
加上explicit
上面可以看出編譯器會為你做一些,隱式的類型轉換工作。這或許會讓你感到方便,但是有時候卻會帶來麻煩。我們來做一個假設:
上面這個類A,只接受整型和字符串型。所以你想傳遞一個字符串 “2” 作為參數來構造一個新的對象。比如 A b = “2”; 然而,你卻寫錯了,雙引號寫成了單引號變成了 A b = ‘2’; 然而這樣並不會報錯。編譯器 會把 字符 ‘2’ 轉型成 整型 也就是它的ascll碼—— 50。為了避免這樣我們不希望的隱式轉換,我們可以加上explicit 關鍵字。
public: explicit A(int i) { a=i; }
這樣就能避免隱式的類型轉換了,當你誤寫成單引號的時候,就會報錯。這樣就只允許顯示的調用單參數構造函數了。如 A a(10); A b("123");
不僅如此,在加上explicit之后,print函數也會報錯了。因為編譯器不會主動調用explicit標識的構造器。這就需要你自己顯示的來調用了:
print(A(10));
當然了,是否應該禁止隱式轉換是沒有定論的,沒有一種放之四海皆准的標准,具體看你的情景需要了。
explicit使用注意事項:
* explicit 關鍵字只能用於類內部的構造函數聲明上。
* explicit 關鍵字作用於單個參數的構造函數(或者除了第一個參數外其余參數都有默認值的多參構造函數),如Circle(int x, int y = 0) 。
* 在C++中,explicit關鍵字用來修飾類的構造函數,被修飾的構造函數的類,不能發生相應的隱式類型轉換,只能以顯示的方式進行類型轉換。
順便一提
explicit關鍵字只用在類內部的聲明中。在外部的實現部分不需要使用。
#include<iostream> using namespace std; class A { public: explicit A(int i); A(char * c) { a=c[0]; } int getValue() { return a; }; private: int a; }; A::A(int i)//無需再指明explicit { a=i; } void print(A a) { cout<<a.getValue(); }; void main() { print(A(10)); }
轉自:http://blog.csdn.net/guodongxiaren/article/details/24455653?utm_source=tuicool&utm_medium=referral