1. 程式人生 > >c++中建構函式初始化的方法以及主要區別

c++中建構函式初始化的方法以及主要區別

一、我的問題是關於初始化C++類成員的。我見過許多這樣的程式碼:

CSomeClass::CSomeClass()

{

x=0;

y=1;

}

而在別的什麼地方則寫成下面的樣子:

CSomeClass::CSomeClass() : x(0), y(1)

{

}

我的一些程式設計師朋友說第二種方法比較好,但他們都不知道為什麼是這樣。你能告訴我這兩種類成員初始化方法的區別嗎?

回答

從技術上說,你的程式設計師朋友是對的,但是在大多數情況下,兩者實際上沒有區別。有兩個原因使得我們選擇第二種語法,它被稱為成員初始化列表:一個原因是必 須的,另一個只是出於效率考慮。

讓我們先看一下第一個原因——必要性。設想你有一個類成員,它本身是一個類或者結構,而且只有一個帶一個引數的建構函式。

class CMember {

public:

CMember(int x) { ... }

};

因為Cmember有一個顯式宣告的建構函式,編譯器不產生一個預設建構函式(不帶引數),所以沒有一個整數就無法建立Cmember的一個例項。

CMember* pm = new CMember; // Error!!

CMember* pm = new CMember(2); // OK

如果Cmember是另一個類的成員,你怎樣初始化它呢?你必須使用成員初始化列表。

class CMyClass {

CMember m_member;

public:

CMyClass();

};

//必須使用成員初始化列表

CMyClass::CMyClass() : m_member(2)

{

...

}

沒有其它辦法將引數傳遞給m_member,如果成員是一個常量物件或者引用也是一樣。根據C++的規則,常量物件和引用不能被賦值,它們只能被初始化。

第二個原因是出於效率考慮,當成員類具有一個預設的建構函式和一個賦值操作符時。MFC的Cstring提供了一個完美的例子。假定你有一個類 CmyClass具有一個Cstring型別的成員m_str,你想把它初始化為 "yada yada. "。你有兩種選擇:

CMyClass::CMyClass() {

// 使用賦值操作符

// CString::operator=(LPCTSTR);

m_str = _T( "yada yada ");

}

//使用類成員列表

// and constructor CString::CString(LPCTSTR)

CMyClass::CMyClass() : m_str(_T( "yada yada "))

{

}

在 它們之間有什麼不同嗎?是的。編譯器總是確保所有成員物件在建構函式體執行之前初始化,因此在第一個例子中編譯的程式碼將呼叫CString:: Cstring來初始化m_str,這在控制到達賦值語句前完成。在第二個例子中編譯器產生一個對CString:: CString(LPCTSTR)的呼叫並將 "yada yada "傳遞給這個函式。結果是在第一個例子中呼叫了兩個Cstring函式(建構函式和賦值操作符),而在第二個例子中只調用了一個函式。在 Cstring的例子裡這是無所謂的,因為預設建構函式是內聯的,Cstring只是在需要時為字串分配記憶體(即,當你實際賦值時)。但是,一般而言, 重複的函式呼叫是浪費資源的,尤其是當建構函式和賦值操作符分配記憶體的時候。在一些大的類裡面,你可能擁有一個建構函式和一個賦值操作符都要呼叫同一個負 責分配大量記憶體空間的Init函式。在這種情況下,你必須使用初始化列表,以避免不要的分配兩次記憶體。在內部型別如ints或者longs或者其它沒有構 造函式的型別下,在初始化列表和在建構函式體內賦值這兩種方法沒有效能上的差別。不管用那一種方法,都只會有一次賦值發生。有些程式設計師說你應該總是用初始 化列表以保持良好習慣,但我從沒有發現根據需要在這兩種方法之間轉換有什麼困難。在程式設計風格上,我傾向於在主體中使用賦值,因為有更多的空間用來格式化和 添加註釋,你可以寫出這樣的語句:x=y=z=0;

或者memset(this,0,sizeof(this));

注意第二個片斷絕對是非面向物件的。

當我考慮初始化列表的問題時,有一個奇怪的特性我應該警告你,它是關於C++初始化類成員的,它們是按照聲 明的順序初始化的,而不是按照出現在初始化列表中的順序。


class CMyClass {

CMyClass(int x, int y);

int m_x;

int m_y;

};

CMyClass::CMyClass(int i) : m_y(i), m_x(m_y)

{

}

你 可能以為上面的程式碼將會首先做m_y=I,然後做m_x=m_y,最後它們有相同的值。但是編譯器先初始化m_x,然後是m_y,,因為它們是按這樣的順 序宣告的。結果是m_x將有一個不可預測的值。我的例子設計來說明這一點,然而這種bug會更加自然的出現。有兩種方法避免它,一個是總是按照你希望它們 被初始化的順序宣告成員,第二個是,如果你決定使用初始化列表,總是按照它們宣告的順序羅列這些成員。這將有助於消除混淆。

相關推薦

c++建構函式初始方法以及主要區別

一、我的問題是關於初始化C++類成員的。我見過許多這樣的程式碼: CSomeClass::CSomeClass() { x=0; y=1; } 而在別的什麼地方則寫成下面的樣子: CSomeClass::CSomeClass() : x(0), y(1) { } 我的一些程式設計師朋友說第

C++建構函式初始列表為什麼會比建構函式賦值要高效

Test { Test(test& t1) { this->t=t1; } private: test t; } 相信很多人跟我一樣,非常困惑為什麼Test構造時會先呼叫test的建構函式,再呼叫test類的賦值操作符。那是因為賦值操作符不能產生新的物件,Te

C++類建構函式初始列表

        建構函式初始化列表以一個冒號開始,接著是以逗號分隔的資料成員列表,每個資料成員後面跟一個放在括號中的初始化式。例如: class CExample {     public:         int a;         float b;      

C++預設建構函式建構函式初始列表

1、預設建構函式和建構函式 (1)建構函式:C++用於構建類的新物件時需要呼叫的函式,該函式無返回型別!(注意:是“無”! 不是空!(void))。 (2)預設建構函式:預設建構函式是在呼叫時不需要顯示地傳入實參的建構函式。 一個類如果自己沒有定義建構函式,則會有一個無參且函式體也是空的

c++初始列表和建構函式初始

初始化和賦值對內建型別的成員沒有什麼的的區別,在成員初始化列表和建構函式體內進行,在效能和結果上都是一樣的。對非內建型別成員變數,因為類型別的資料成員的資料成員物件在進入函式體前已經構造完成,也就是說在成員初始化列表處進行構造物件的工作,呼叫建構函式,在進入函式體之後,進行的是對已經構造好的類物件的賦值,又呼

C++建構函式初始列表與建構函式的賦值的區別

C++類中成員變數的初始化有兩種方式:          建構函式初始化列表和建構函式體內賦值。下面看看兩種方式有何不同。          成員變數初始化的順序是按照在那種定義的順序。 1、內部資料型別(char,int……指標等) class Animal { publ

C++類使用建構函式初始類表和建構函式函式賦值的區別

參考:http://www.360doc.com/content/13/0607/19/1317564_291331713.shtml C++ Primer中在講建構函式初始化列表的時候有這麼一段話:  無論是在建構函式初始化列表中初始化成員,還是在建構函式體中對它們賦

C++ 建構函式初始呼叫順序及類函式內部巢狀函式情況

C++建構函式初始化順序 C++建構函式按下列順序被呼叫:(1、2、3、4是按照優先順序順序來的!) (1)任何虛擬基類的建構函式按照它們被繼承的順序構造; (2)任何非虛擬基類的建構函式按照它們被繼承的順序構造; (3)任何成員物件的建構函式按照它們宣告的順序呼叫;(如果成員物件有前面出現

重新認識memset函式c++陣列的初始賦值

下面就好好重新認識一下這個函式,自己寫了測試的程式碼,memset到底怎麼用呢? 先貼測試程式碼: #include<iostream> #include<cstdio> #include<algorithm> #include<string>

c++ 子類建構函式初始及父類構造初始

  我們知道,構造方法是用來初始化類物件的。如果在類中沒有顯式地宣告建構函式,那麼編譯器會自動建立一個預設的建構函式;並且這個預設的建構函式僅僅在沒有顯式地宣告建構函式的情況下才會被建立建立。   建構函式與父類的其它成員(成員變數和成員方法)不同,它不能被子類繼承。因此,在建立子類物件時,為了初始化從父類

C++派生類物件建構函式初始順序

答:(1)先呼叫基類中的建構函式(如果有多個基類,根據繼承時宣告的順序進行初始化) (2)再呼叫成員類中的建構函式(如果有多個成員類,根據其宣告的順序進行初始化) (3)最後初始化派生類本身的建構函式 例項分析: #include<iostream>   

C++程式設計-第5周 用建構函式初始

看完書再做發現很簡單,只是自己一開始就產生了畏懼心理。——一位正在進步的同學這樣總結這句話讓我很有感觸。這就是成長,這句話是留給有心人自己說出來的。——迂者感到很欣慰【專案1】設計三角形類,通過增加建構函式,使物件在定義時能夠進行初始化#include<iostream

java 建構函式 成員函式初始順序 以及多型的建構函式的呼叫順序

對於JAVA中類的初始化是一個很基礎的問題,其中的一些問題也是易被學習者所忽略。當在編寫程式碼的時候碰到時,常被這些問題引發的錯誤,感覺莫名其妙。 而且現在許多大公司的面試題,對於這方面的考查也是屢試不爽。不管基於什麼原因,我認為,對於java類中的初始化問

C++建構函式和解構函式顯式呼叫的方法

C++ new用法的正確方法應該如何操作?在這篇文章中我們將會根據其兩種不同的含義分別介紹一下它的影音方法,希望能給大家帶來一些幫助。C++程式語言中有一種叫做new的術語。不過這一概念是比較模糊的,有些人把它理解為new函式,但是有的人又會把它理解為new運算子。那麼它的真正含義又是如何的呢?在這裡我們將會

C++ write constructors using a constructor initializer 建構函式 初始資料

C++‘s constructor is different than java or C#. A C++ constructor like any other function, has a name, a parameter list, and a function b

結構體使用建構函式初始

struct作為資料結構的實現體,它預設所有結構成員預設都是Public,而類的變數和常量數則預設為Private,不過其他類成員預設都是Public。 C++結構體的繼承預設是public,而c++類的繼承預設是private。 所以結構體中使用建構函式也是合理的。 st

Unity3D開發繼承了MonoBehaviour的類,不要再建構函式初始

MonoBehaviour派生出來的類會作為Unity3D中的Component掛載在GameObject上,而GameObject會在編輯器的多個地方被顯示,如場景編輯器內、Prefab選中時等,這些時候都需要呼叫它們的建構函式來初始化成員變數的預設值,以便在編輯器中顯示它

建構函式初始器this

this可以用作建構函式的初始化器,這個關鍵字指定在呼叫指定的建構函式之前,.NET例項化過程對當前類使用非預設的建構函式 1 public class BaseClass 2 { 3 public BaseClass() { } 4 public BaseClass(i

建構函式初始列表

表現形式有三種 當前類進行初始化,尤其是當成員變數是一個const或者一個引用型別 class Demo { private: int b; public: Demo(int a):b(a){}; } 需

C++只能使用初始列表(只能是初始)不能使用賦值的情況

(1)當類中含有const(常量)、reference(引用)成員變數時,只能初始化,不能對它們進行賦值;      常量不能被賦值,只能被初始化,所以必須在初始化列表中完成,C++引用也一定要初始化,所以必須在初始化列表中完成。 (2)基類的建