Qt/C++ 構造函數與explicit


 

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));

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

一般而言,顯示調用構造器,能避免一些麻煩,讓程序員手動來管理。很多人說C++難,因為很多東西對於程序員來說不是透明的,比如內存釋放什么的,這個顯式調用也是需要程序員自己動手的。然而我感覺這正是C++的魅力所在,C++給了程序員幾乎等同於上帝的權力,所有一切都能自己掌控,還比如運算符重載的權力,甚至像Qt這樣可以自定義slot和signal關鍵字(其實是宏),這在其他高級語言里是不可想象的。當然了,語言這東西,是仁者見仁智者見智的。沒必要爭論優劣。我一直認為的是:沒有最優秀的語言,只有最合適的語言。編程語言本身沒有優劣之分,但是不同程序員對於不同語言確有好惡之別。 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM