欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++中的對象初始化操作代碼

 更新時間:2021年12月20日 15:18:09   作者:wenglabs  
對象初始化可以分為默認初始化、直接初始化、拷貝初始化以及值初始化。本文重點給大家介紹C++中的對象初始化操作代碼,感興趣的朋友跟隨小編一起看看吧

當對象在創(chuàng)建時獲得了一個特定的值,我們說這個對象被初始化。初始化不是賦值,初始化的含義是創(chuàng)建變量賦予其一個初始值,而賦值的含義是把當前值擦除,而以一個新值來替代。對象初始化可以分為默認初始化、直接初始化、拷貝初始化以及值初始化。

// new edit on 2020.7.23
#pragma once
#include <iostream>
using namespace std;

class ClassTest {
 public:

  //定義默認構造函數(shù)
  ClassTest()  
  {
    c[0] = '\0';
    cout << "1) ClassTest()" << endl;
  }

  // 直接初始化
  ClassTest(const char* pc)  
  {
    strcpy_s(c, pc);
    cout << "2) ClassTest (const char *pc)" << endl;
  }

  //復制/拷貝構造函數(shù)
  ClassTest(const ClassTest& ct)  
  {
    strcpy_s(c, ct.c);
    cout << "3) ClassTest(const ClassTest& ct)" << endl;
  }

  //重載賦值操作符
  ClassTest& operator=(const ClassTest& ct)  
  {
    strcpy_s(c, ct.c);
    cout << "4) ClassTest& operator=(const ClassTest &ct)" << endl;
    return *this;
  }

 private:
  char c[256];
};

ClassTest func(ClassTest temp) { return temp; }

int demo_test() {
  cout << "ct1: ";
  ClassTest ct1("ab");  // 直接初始化
  cout << "ct2: ";
  ClassTest ct2 = "ab"; // 直接初始化

  /*
  輸出說明:關于編譯優(yōu)化:

  ClassTest ct2 = "ab";
  它本來是要這樣來構造對象的:
  首先,調用構造函數(shù)ClassTest(const char *pc)函數(shù)創(chuàng)建一個臨時對象。
  然后,調用復制構造函數(shù),把這個臨時對象作為參數(shù),構造對象ct2。然而,編譯也發(fā)現(xiàn),復制構造函數(shù)是
  公有的,即你明確地告訴了編譯器,你允許對象之間的復制,而且此時它發(fā)現(xiàn)可以通過直接調用重載的
  構造函數(shù)ClassTest(const char *pc)來直接初始化對象,而達到相同的效果,所以就把這條語句優(yōu)化為
  ClassTest ct2("ab")。
  */
  cout << "ct3: ";
  ClassTest ct3 = ct1;  // 復制初始化
  cout << "ct4: ";
  ClassTest ct4(ct1);   // 復制初始化
  cout << "ct5: ";
  ClassTest ct5 = ClassTest();  // 默認構造函數(shù)
  
  cout << "\nct6: "; // 依次調用 1)、2)、4),即默認、直接、重載
  ClassTest ct6;  
  ct6 = "caoyan is a good boy!"; 

  cout << "\nct7: ";  
  ClassTest ct7;  // 依次調用 1)、3)、3)、4)
  ct7 = func(ct6);

  return 0;
}

old code:

// (1)默認初始化
int i1;//默認初始化,在函數(shù)體之外(初始化為0)  


int f(void)  
{  
int i2;//不被初始化,如果使用此對象則報錯  
}  

string empty;//empty非顯示的初始化為一個空串,調用的是默認構造函數(shù)  

// (2)拷貝初始化
string str1(10,'9');//直接初始化  
string str2(str1);//直接初始化  
string str3 = str1;//拷貝初始化  

 // (3)值初始化
vector<int> v1(10);//10個元素,每個元素的初始化為0  
vector<string> v2(10);//10個元素,每個元素都為空   

int *pi = new int;//pi指向一個動態(tài)分配的,未初始化的無名對象  
string *ps = new string;//初始化為空string  
int *pi = new int;//pi指向一個未初始化的int  

int *pi = new int(1024);//pi指向的對象的值為1024  
string *ps = new string(10,'9');//*ps為"9999999999"  


string *ps1 = new string;//默認初始化為空string  
string *ps2 = new string();//值初始化為空string  
int *pi1 = new int;//默認初始化  
int *pi2 = new int();//值初始化為0   

1、C++ Copy初始化

在《inside the c++ object model》一書中談到copy constructor的構造操作,有三種情況下,會以一個object的內容作為另一個object的初值:

  • 第一種情況:XXaa=a;第二種情況:XXaa(a);
  • 第三種情況:externfun(XXaa);fun(a)函數(shù)調用
  • 第四種情況:XXfun(){...};XXa=fun();函數(shù)返回值的時候

下面我們就上述的四種情況來一一驗證

#include <iostream>
using namespace std;

class ClassTest {
public:
  ClassTest() //定義默認構造函數(shù)
  {
    c[0] = '\0';
    cout << "ClassTest()" << endl;
  }
  ClassTest(const char *pc) // 直接初始化
  {  
    strcpy_s(c, pc);
    cout << "ClassTest (const char *pc)" << endl;
  }
  ClassTest(const ClassTest &ct) //復制構造函數(shù)
  {
    strcpy_s(c, ct.c);
    cout << "ClassTest(const ClassTest& ct)" << endl;
  }
  ClassTest &operator=(const ClassTest &ct)  //重載賦值操作符
  {
    strcpy_s(c, ct.c);
    cout << "ClassTest& operator=(const ClassTest &ct)" << endl;
    return *this;
  }

private:
  char c[256];
};

ClassTest func(ClassTest temp) { return temp; }

int main() {
  cout << "ct1: ";
  ClassTest ct1("ab"); //直接初始化
  cout << "ct2: ";
  ClassTest ct2 = "ab"; //復制初始化
  /*輸出說明:
  ClassTest ct2 = "ab";
  它本來是要這樣來構造對象的:
  首先,調用構造函數(shù)ClassTest(const char *pc)函數(shù)創(chuàng)建一個臨時對象。
  然后,調用復制構造函數(shù),把這個臨時對象作為參數(shù),構造對象ct2。然而,編譯也發(fā)現(xiàn),復制構造函數(shù)是
  公有的,即你明確地告訴了編譯器,你允許對象之間的復制,而且此時它發(fā)現(xiàn)可以通過直接調用重載的
  構造函數(shù)ClassTest(const char *pc)來直接初始化對象,而達到相同的效果,所以就把這條語句優(yōu)化為
  ClassTest ct2("ab")。
  */
  cout << "ct3: ";
  ClassTest ct3 = ct1; //復制初始化
  cout << "ct4: ";
  ClassTest ct4(ct1); //直接初始化
  cout << "ct5: ";
  ClassTest ct5 = ClassTest(); //復制初始化
  cout << "ct6: ";
  ClassTest ct6; //復制初始化
  ct6 = "caoyan is a good boy!";
  cout << "ct7: ";
  ClassTest ct7;
  ct7 = func(ct6);
  return 0;
}

測試結果:

我們可以看到,比較復雜的是ct6和ct7,其中ct6還是比較好理解的,ct7這種情況比較難懂,為什么會有兩個拷貝構造函數(shù)的調用????

第一次拷貝構造函數(shù)的調用:第一次很簡單,是因為函數(shù)參數(shù)的傳遞,將ct6作為參數(shù)傳遞給temp,用ct6的值初始化temp會調用拷貝構造函數(shù);

第二次拷貝構造函數(shù)的調用:因為要返回一個ClassTest對象,我們的編譯器怎么做????首先它將temp對象拷貝到func函數(shù)的上一級棧幀中,它的上一級棧幀是main函數(shù)的棧幀,那么當函數(shù)返回時,參數(shù)出棧,temp對象的內存空間就會被收回,但是它的值已經(jīng)被拷貝到main棧幀的一個預留空間中,所以從temp到預留空間的拷貝也是調用拷貝構造函數(shù),最后一步就是給ct7賦值,毫無疑問調用賦值構造函數(shù);對棧幀不同的同學可以看看《程序員的自我修養(yǎng)》一書,里面講得很詳細!

2、初始化列表、構造函數(shù)與=賦值之間的區(qū)別

總所周知,C++對象在創(chuàng)建之時,會由構造函數(shù)進行一系列的初始化工作。以沒有繼承關系的單個類來看,除了構造函數(shù)本身的產(chǎn)生與指定,還涉及到初始化步驟,以及成員初始化方式等一些細節(jié),本篇筆記主要對這些細節(jié)進行介紹,弄清C++對象在初始化過程中一些基本運行規(guī)則。

構造函數(shù)指定

通常,我們在設計一個類的時候,會為這個類編寫對應的default constructor、copy constructor、copy assignment operator,還有一個deconstructor。即便我們僅僅編寫一個空類,編譯器在編譯時仍舊會為其默認聲明一個default constructor、copy constructor、copy assignment operator與deconstructor,如果在代碼里面存在著它們的使用場景,那么這個時候編譯器才會創(chuàng)建它們。

class MyCppClass {}

一旦我們?yōu)橐粋€類編寫了default constructor,那么編譯器也就不會為其默認生成default constructor,對于其他幾個函數(shù)也一樣。對于編譯器默認生成的constructor來說,它會以一定規(guī)則對每一個數(shù)據(jù)成員進行初始化??紤]到成員初始化的重要性,在編寫自己的constructor時就需要嚴謹認真了,特別是在類的派生與繼承情況下這點顯得尤為重要。對于copy constructor和assignment operator的運用場景,這里不得不多說一點,見如下代碼:

#include <iostream>
 
using std::cout;
using std::endl;
 
class MyCppClass
{
public:
    MyCppClass()
    {
        std::cout <<"In Default Constructor!" <<std::endl;
    }
 
    MyCppClass(const MyCppClass& rhs)
    {
        std::cout <<"In Copy Constructor!" <<std::endl;
    }
 
    MyCppClass& operator= (const MyCppClass& rhs)
    {
        std::cout <<"In Copy Assignment Operator!" <<std::endl;
 
        return *this;
    }
};
 
int main()
{
    MyCppClass testClass1;                 // default constructor
    MyCppClass testClass2(testClass1);     // copy constructor
    testClass1 = testClass2;               // copy assignment operator
 
    MyCppClass testClass3 = testClass1;    // copy constructor
 
    return 0;
}

執(zhí)行結果:

這里需要注意的是,一般情況下我們總是以為在‘='運算符出現(xiàn)的地方都是調用copy assignment operator,上

面這種情況卻是個例外。也就是,當一個新對象被定義的時候,即便這個時候是使用了'='運算符,它真實調用的是初始化函數(shù)copy constructor,而不是調用copy assignment operator去進行賦值操作。

Why初始化列表

一個對象在初始化時包括了兩個步驟:

首先,分配內存以保存這個對象;

其次,執(zhí)行構造函數(shù)。

在執(zhí)行構造函數(shù)的時候,如果存在有初始化列表,則先執(zhí)行初始化列表,之后再執(zhí)行構造函數(shù)的函數(shù)體。那么,為什么會引入初始化列表呢?

C++與C相比,在程序組織上由“以函數(shù)為基本組成單位的面向過程”變遷到“基于以類為中心的面向對象”,與此同時類也作為一種復合數(shù)據(jù)類型,而初始化列表無非就是進行一些數(shù)據(jù)的初始化工作??紤]到這里,也可以較為自然的推測初始化列表與類這種數(shù)據(jù)類型的初始化有著關聯(lián)。

在引入初始化列表之后,一個類對應數(shù)據(jù)成員的初始化就存在有兩種方式。下面是類的數(shù)據(jù)成員類型分別為內置類型、自定義類型時的一個對比。?

// 數(shù)據(jù)成員類型為內置類型
class MyCppClass
{
public:
    // 賦值操作進行成員初始化
    MyCppClass 
    {
        counter = 0;
    }
    
    // 初始化列表進行成員初始化
    MyCppClass : counter(0)
    {
    }

private:
    int    counter;
}

當類的數(shù)據(jù)成員類型為內置類型時,上面兩種初始化方式的效果一樣。當數(shù)據(jù)成員的類型同樣也為一個類時,初始化的過程就會有不一樣的地方了,比如:

// 數(shù)據(jù)成員類型為自定義類型:一個類
class MyCppClass
{
public:
    // 賦值操作進行成員初始化
    MyCppClass(string name) 
    {
        counter = 0;
        theName = name;
    }

    // 初始化列表進行成員初始化
    MyCppClass : counter(0), theName(name)
    {
    }

private:
    int    counter;
    string theName;
}

在構造函數(shù)體內的theName = name這條語句,theName先會調用string的default constructor進行初始化,之后再調用copy assignment opertor進行拷貝賦值。而對于初始化列表來說,直接通過copy constructor進行初始化。

明顯起見,可以通過如下的代碼進行測試。

#include <iostream>
#include <string>
 
class SubClass
{
public:
    SubClass()
    {
        std::cout <<" In SubClass Default Constructor!" <<std::endl;
    }
 
    SubClass(const SubClass& rhs)
    {
        std::cout <<" In SubClass Copy Constructor!" <<std::endl;
    }
 
    SubClass& operator= (const SubClass& rhs)
    {
        std::cout <<" In SubClass Copy Assignment Operator!" <<std::endl;
 
        return *this;
    }
};
 
class BaseClass
{
public:
    BaseClass(const SubClass &rhs)
    {
        counter = 0;
        theBrother = rhs;
        std::cout <<" In BaseClass Default Constructor!" <<std::endl;
    }
 
    BaseClass(const SubClass &rhs, int cnt):theBrother(rhs),counter(cnt)
    {
        std::cout <<" In BaseClass Default Constructor!" <<std::endl;
    }
 
    BaseClass(const BaseClass& rhs)
    {
        std::cout <<" In BaseClass Copy Constructor!" <<std::endl;
    }
 
    BaseClass& operator= (const BaseClass& rhs)
    {
        std::cout <<" In BaseClass Copy Assignment Operator!" <<std::endl;
 
        return *this;
    }
private:
    int counter;
    SubClass theBrother;
};
 
int main()
{
    SubClass subClass;
 
    std::cout <<"\nNo Member Initialization List: " <<std::endl;
    BaseClass BaseClass1(SubClass);
 
    std::cout <<"\nMember Initialization List: " <<std::endl;
    BaseClass BaseClass2(SubClass, 1);
 
    return 0;
}

執(zhí)行結果:

也就是,在涉及到自定義類型初始化的時候,使用初始化列表來完成初始化在效率上會有著更佳的表現(xiàn)。這也是初始化列表的一大閃光點。即便對于內置類型,在一些情況下也是需要使用初始化列表來完成初始化工作的,比如const、references成員變量。這里有篇筆記,對初始化列表有著非常詳盡的描述。

幾個初始化名詞

在閱讀《Accelerated C++》中文版時,總是碰到“缺省初始化”、“隱式初始化”以及“數(shù)值初始化”,最初在理解這幾個名詞的時候幾費周折,總覺得為什么一個初始化操作造出了如此多的名詞,為此沒少花時間來弄清楚它們之間的關系。

為了更好的理解它們,先對C++當中的數(shù)據(jù)類型進行簡單劃分。在C++里面,數(shù)據(jù)類型大致可以分為兩種:第一種是內置類型,比如float, int, double等;第二種是自定義類型,也就是我們常用的class, struct定義的類。在對這些類型的數(shù)據(jù)進行初始化時,差別就體現(xiàn)出來了:對于內置類型,在使用之前必須進行顯示的初始化,而對于自定義類型,初始化責任則落在了構造函數(shù)身上。

int x = 0;          // 顯示初始化x
SubClass subClass;  // 依賴SubClass的default constructor進行初始化

上面的名詞“缺省初始化”描述的就是當內置類型或者自定義類型的數(shù)據(jù)沒有進行顯示初始化時的一種初始化狀態(tài)。而“隱式初始化”描述的是在該狀態(tài)下面進行的具體操作方式,比如對于內置類型來說,缺省初始化狀態(tài)下進行的隱式初始化實際上是未定義的,而自定義類型的隱式初始化則依賴于其constructor。

前面提到過C++不保證內置類型的初始化,但是當內置類型在作為一個類的成員時,在某些特定的條件下該內置類型的成員會被編譯器主動進行初始化,對于這個過程也就是所謂的數(shù)值初始化。在《Accelerated C++》當中列出了如下的幾種情況:

  1. 對象被用來初始化一個容器元素
  2. 為映射表添加一個新元素,對象是這個添加動作的副作用
  3. 定義一個特定長度的容器,對象為容器的元素

測試如下:

#include <iostream> 
#include <vector> 
#include <map> 
#include <string> 
 
using std::cout; 
using std::endl; 
using std::vector; 
using std::map; 
using std::string; 
 
class NumbericInitTestClass 
{ 
public: 
    void PrintCounter() 
    { 
        cout <<"counter = " <<counter <<endl; 
    } 
private: 
    int counter; 
}; 
 
 
int main() 
{ 
    NumbericInitTestClass tnc; 
    tnc.PrintCounter(); 
 
    map<string, int> mapTest; 
    cout <<mapTest["me"] <<endl; 
 
    vector<NumbericInitTestClass> vecNumbericTestClass(1); 
    vecNumbericTestClass[0].PrintCounter(); 
 
    return 0; 
}

對于沒有進行初始化的內置類型,是一個未定義的值2009095316,而對于2, 3種情況來說,均被初始化為0,對于第1種情況我還沒有想到合適的場景。

回過頭想想,為了書中的一些相似的名詞,去想辦法把它們湊在一起總是顯得有些牽強附會:)一些規(guī)則這里附上幾條有關初始化的基本規(guī)則,它們多來源于《Effective C++》

1. 為內置型對象進行手工初始化,因為C++不保證初始化它們。

2. 構造函數(shù)最好使用成員初值列(member initialization list),而不要在構造函數(shù)體內使用賦值操作。初值列列出的成員變量,其排列次序應該和它們在class中聲明的次序相同。

3. C++不喜歡析構函數(shù)吐出異常。

4. 在構造函數(shù)與析構函數(shù)期間不要調用virtual函數(shù),因為這類調用從不下降至derived class。

5. copying函數(shù)應該確保復制“對象內所有成員變量”及“所有base class成分”。

參考文章

C++中對象初始化方式

c++類對象初始化方式總結

Peter87, C++中的對象初始化

到此這篇關于C++中的對象初始化的文章就介紹到這了,更多相關C++對象初始化內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 淺析C/C++變量在內存中的分布

    淺析C/C++變量在內存中的分布

    變量在內存地址的分布為:堆-棧-代碼區(qū)-全局靜態(tài)-常量數(shù)據(jù)。同一區(qū)域的各變量按聲明的順序在內存的中依次由低到高分配空間(只有未賦值的全局變量是個例外)
    2013-09-09
  • C/C++函數(shù)指針深入探究

    C/C++函數(shù)指針深入探究

    函數(shù)指針是一個指針變量,它可以存儲函數(shù)的地址,然后使用函數(shù)指針,下面這篇文章主要給大家介紹了關于C語言進階教程之函數(shù)指針的相關資料,需要的朋友可以參考下
    2022-08-08
  • 詳解C++ 模板編程

    詳解C++ 模板編程

    模板(template)是C++實現(xiàn)泛型(Generics)和元編程(Meta Programming)的基礎。本文拋磚引玉,簡要介紹C++模板編程,不足之處敬請指正。
    2020-09-09
  • C語言模擬實現(xiàn)庫函數(shù)詳解

    C語言模擬實現(xiàn)庫函數(shù)詳解

    C語言庫函數(shù)是把自定義函數(shù)放到庫里,是別人把一些常用到的函數(shù)編完放到一個文件里,供程序員使用,下面讓我們一起來詳細了解它
    2022-07-07
  • C語言進階教程之字符串&內存函數(shù)

    C語言進階教程之字符串&內存函數(shù)

    對于字符,在計算機內部都是用數(shù)字(字符編碼)來表示的,而字符串是“字符連續(xù)排列”的一種表現(xiàn),這篇文章主要給大家介紹了關于C語言進階教程之字符串&內存函數(shù)的相關資料,需要的朋友可以參考下
    2021-09-09
  • 用C語言實現(xiàn)推箱子游戲實例

    用C語言實現(xiàn)推箱子游戲實例

    大家好,本篇文章主要講的是用C語言實現(xiàn)推箱子游戲實例,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-01-01
  • 詳解C語言中的符號常量、變量與算術表達式

    詳解C語言中的符號常量、變量與算術表達式

    這篇文章主要介紹了C語言中的符號常量、變量與算術表達式,是C語言入門學習中的基礎知識,需要的朋友可以參考下
    2015-11-11
  • 一起來學習C++中remove與erase的理解

    一起來學習C++中remove與erase的理解

    這篇文章主要為大家詳細介紹了C++的remove與erase,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • C/C++中的static關鍵字詳解

    C/C++中的static關鍵字詳解

    這篇文章主要為大家詳細介紹了 C/C++中的static關鍵字,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • C語言零基礎入門(1)

    C語言零基礎入門(1)

    這篇文章主要為大家詳細介紹了C語言零基礎入門的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03

最新評論