1. 程式人生 > >C++類使用建構函式初始化類表和建構函式函式體中賦值的區別

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

參考:http://www.360doc.com/content/13/0607/19/1317564_291331713.shtml

C++ Primer中在講建構函式初始化列表的時候有這麼一段話: 

無論是在建構函式初始化列表中初始化成員,還是在建構函式體中對它們賦值,最終結果是相同的。不同之處在於,使用建構函式初始化列表的版本初始化資料成員,沒有定義初始化列表的建構函式版本在建構函式體中對資料成員賦值。  
請問這裡的初始化資料成員與對資料成員賦值的含義是什麼?有什麼區別?  
我知道在資料成員有預設建構函式時是有不同的,但對其他型別的成員呢?其他型別成員的初始化和賦值有區別嗎? 
======================================================================================== 是這個意思: 
首先把資料成員按型別分類 
1。內建資料型別,複合型別(指標,引用) 2。使用者定義型別(類型別)  
分情況說明: 
對於型別1,在成員初始化列表和建構函式體內進行,在效能和結果上都是一樣的 對於型別2,結果上相同,但是效能上存在很大的差別 
因為類型別的資料成員物件在進入函式體是已經構造完成,也就是說在成員初始化列表處進行構造物件的工作,這是呼叫一個建構函式,在進入函式體之後,進行的是對已經構造好的類物件的賦值,又呼叫個拷貝賦值操作符才能完成(如果並未提供,則使用編譯器提供的預設按成員賦值行為)  
舉個例說明 class A; class B {public: B(){a = 3;} private: A a; } 
 class A {public: A(){} 
A(int){value = 3;} int value; }  
像上面,我們使a物件的value為3,呼叫一個A的建構函式+一個預設拷貝賦值符,才達到目的 
B::B():a(3){} 
像這樣,只調用了一個建構函式就達到了所需的物件啦,所以效能好的  
轉載他人一篇 
我的問題是關於初始化C++類成員的。我見過許多這樣的程式碼(包括在你的欄目中也見到過): 
CSomeClass::CSomeClass() { x=0; y=1; } 
而在別的什麼地方則寫成下面的樣子: CSomeClass::CSomeClass() : x(0), y(1) { } 
我的一些程式設計師朋友說第二種方法比較好,但他們都不知道為什麼是這樣。你能告訴我這兩種類成員初始化方法的區別嗎? 
回答 
從技術上說,你的程式設計師朋友是對的,但是在大多數情況下,兩者實際上沒有區別。有兩個原因使得我們選擇第二種語法,它被稱為成員初始化列表:一個原因是必須的,另一個只是出於效率考慮。 
讓我們先看一下第一個原因——必要性。設想你有一個類成員,它本身是一個類或者結構,而且只有一個帶一個引數的建構函式。 classCMember { public: 
CMember(int x) { ... } }; 
因為Cmember有一個顯式宣告的建構函式,編譯器不產生一個預設建構函式(不帶引數),所以沒有一個整數就無法建立Cmember的一個例項。 CMember* pm = new CMember; // Error!! CMember* pm = new CMember(2); // OK 
如果Cmember是另一個類的成員,你怎樣初始化它呢?你必須使用成員初始化列表。 classCMyClass { CMemberm_member; public: CMyClass(); }; 
//必須使用成員初始化列表 
CMyClass::CMyClass() : m_member(2)

{ ••• } 
沒有其它辦法將引數傳遞給m_member,如果成員是一個常量物件或者引用也是一樣。根據C++的規則,常量物件和引用不能被賦值,它們只能被初始化。 
第二個原因是出於效率考慮,當成員類具有一個預設的建構函式和一個賦值操作符時。MFC的Cstring提供了一個完美的例子。假定你有一個類CmyClass具有一個Cstring型別的成員m_str,你想把它初始化為"yadayada."。你有兩種選擇: CMyClass::CMyClass() { // 使用賦值操作符 
// CString::operator=(LPCTSTR); m_str = _T("yadayada"); } 
//使用類成員列表 
// and constructor CString::CString(LPCTSTR) CMyClass::CMyClass() : m_str(_T("yadayada")) { } 

在它們之間有什麼不同嗎?是的。編譯器總是確保所有成員物件在建構函式體執行之前初始化,因此在第一個例子中編譯的程式碼將呼叫CString:: Cstring來初始化m_str,這在控制到達賦值語句前完成。在第二個例子中編譯器產生一個對CString:: CString(LPCTSTR)的呼叫並將"yadayada"傳遞給這個函式。結果是在第一個例子中呼叫了兩個Cstring函式(建構函式和賦值操作符),而在第二個例子中只調用了一個函式。在Cstring的例子裡這是無所謂的,因為預設建構函式是內聯的,Cstring只是在需要時為字串分配記憶體(即,當你實際賦值時)。但是,一般而言,重複的函式呼叫是浪費資源的,尤其是當建構函式和賦值操作

符分配記憶體的時候。在一些大的類裡面,你可能擁有一個建構函式和一個賦值操作符都要呼叫同一個負責分配大量記憶體空間的Init函式。在這種情況下,你必須使用初始化列表,以避免不要的分配兩次記憶體。在內部型別如ints或者longs或者其它沒有建構函式的型別下,在初始化列表和在建構函式體內賦值這兩種方法沒有效能上的差別。不管用那一種方法,都只會有一次賦值發生。有些程式設計師說你應該總是用初始化列表以保持良好習慣,但我從沒有發現根據需要在這兩種方法之間轉換有什麼困難。在程式設計風格上,我傾向於在主體中使用賦值,因為有更多的空間用來格式化和添加註釋,你可以寫出這樣的語句:x=y=z=0; 或者memset(this,0,sizeof(this)); 注意第二個片斷絕對是非面向物件的。 
當我考慮初始化列表的問題時,有一個奇怪的特性我應該警告你,它是關於C++初始化類成員的,它們是按照宣告的順序初始化的,而不是按照出現在初始化列表中的順序。 classCMyClass { CMyClass(int x, int y); intm_x; intm_y; }; 
CMyClass::CMyClass(inti) : m_y(i), m_x(m_y) { } 
你可能以為上面的程式碼將會首先做m_y=I,然後做m_x=m_y,最後它們有相同的值。但是編譯器先初始化m_x,然後是m_y,,因為它們是按這樣的順序宣告的。結果是m_x將有一個不可預測的值。我的例子設計來說明這一點,然而這種bug會更加自然的出現。有兩種方法避免它,一個是總是按照你希望它們被初始化的順序宣告成員,第二個是,如果你決定使用初始化列表,總是按照它們宣告的順序羅列這些成員。這將有助於消除混淆。

相關推薦

C++使用建構函式初始建構函式函式區別

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

python中用函式初始變數

今天在寫python的時候遇到一個問題:定義了一個list型別的類變數,但是這個list需要在初始化的時候給它加很多的url進去.這樣的話我們就需要用倒函數了.結果自己剛開始這樣寫的: class TianyaSpider(CrawlSpider): def init_st

函式指標,轉移回撥函式的理解

函式指標 函式指標顧名思義就是將函式看做一個指標,用一個指標來儲存函式的地址 函式指標的用法: 函式指標的正確寫法是  void (*p1)() 而 void *p2() 是無法存放函式指標的,因為這是返回值為指標的函式,p1先與*結合,說明p1是一個指標,指標指向一個

C結構使用的冒號點號

根據論壇中,別人的回答,總結試驗的成果 1、其中位域列表的形式為: 型別說明符 位域名:位域長度 struct bs {  int a:8;  int b:2;  int c:6; }data; 說明data為bs變數,共佔兩個位元組。其中位域a佔8位,位域b佔2位,位域c

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

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

C++建構函式初始列表

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

C++】利用建構函式物件進行初始

一、物件的初始化 每一個物件都應當在它建立之時就有就有確定的內容,否則就會失去物件的意義。 class Time { int hour = 0; int min = 0; int sec = 0; }; 這種是錯誤的,類並不是一個實體,並不佔儲存空間,顯然無處容納

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

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

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

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

建構函式初始列表

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

CPP建構函式初始附圖

// Construction.cpp : 定義控制檯應用程式的入口點。 // //a)類無建構函式如不初始化賦值,其屬性的值都是cccc. //b)類有建構函式如不初始化賦值,其屬性的值都是0000. #include “stdafx.h” class SDate { /* int n

三種情況必須在建構函式初始列表初始

//const成員的初始化只能在建構函式的初始化列表中進行 //引用成員的初始化也只能在建構函式的初始化列表中進行 class object { public:  object(int num = 0 ,int knum = 0):num_(num),knum_(knum),refnum_(num)  {

的成員變數 宣告順序 決定 初始順序(建構函式初始列表不影響)

類成員的宣告順序決定初始化順序; 建構函式初始化列表不影響初始化順序; C++初始化類成員時,是按照宣告的順序初始化的,而不是按照出現在初始化列表中的順序。 class B { public: //m_b = 2,m_a = 1 B():m

C++ 模板的static變數函式 初始

關鍵詞: C++ 模板 static 變數 函式  初始化 這篇文章主要介紹關於模板類中如果有static變數如何初始化問題。 重要:如果不初始化static變數,那麼編譯可能沒有問題,但是程式有問題,可能編譯有問題,但是不能確定是初始化問題。 #include"ios

利用建構函式物件進行初始

1.物件在建立時必須有確定的內容,否則就失去物件的意義了。類是一個實體,不能在類宣告中對資料成員初始化; 2.可以像結構體一樣,在定義物件的時候用花括號初始化 3. c++提供了建構函式來處理物件的初始化。建構函式是特殊的成員函式,它不需要使用者來呼叫,而是在建立物件時自動

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

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

C++構造函數對成員變量初始,使用初始列表構造函數內部直接 的差別

初始化列表 不能 構造 調用 ron 二次 art size strong 初始化和賦值對內置類型的成員沒有什麽大的差別,像任一個構造函數都能夠。但有的時候必須用帶有初始化列表的構造函數: (1) 成員類型是沒有默認構造函數的類。若沒有提供顯式初始化時,則編譯器隱式

c++ 用冒號初始對象(成員初始列表)

函數 ace end -1 box 技術分享 對象 mage stream c++類 用冒號初始化對象(成員初始化列表) 成員初始化的順序不同於它們在構造函數初始化列表中的順序,而與它們在類定義中的順序相同 #include<iostream> int n=0

C++指針初始

類成員函數 類指針 沒有 int mar 但是 編譯器 public 解釋 上面的代碼會打印“A”。 C++ 類指針定義的時候沒有初始化的時候,居然可以安全的調用類內部的成員函數而不出錯。 在網上查了一下: 初始化為NULL的類指針可以安全的調用不涉及類成員變量的類成

C++ 物件陣列初始

如何給類中定義了帶有引數的建構函式的物件陣列賦值 class A { public: A(){}//無參建構函式0 A(int i){}//有參建構函式1 A(int i,int j){}//有參建構函式2 }; int main() { A a,b[5];