最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++踩坑實(shí)戰(zhàn)之構(gòu)造和析構(gòu)函數(shù)

 更新時(shí)間:2021年07月26日 11:54:33   作者:Just for Life.  
不論是構(gòu)造函數(shù),還是析構(gòu)函數(shù),都是C++、C#語(yǔ)言相對(duì)于其他語(yǔ)言而言特殊的地方,它是為了方便類(lèi)中對(duì)象的初始化,這篇文章主要給大家介紹了關(guān)于C++踩坑實(shí)戰(zhàn)之構(gòu)造和析構(gòu)函數(shù)的相關(guān)資料,需要的朋友可以參考下

前言

我是練習(xí)時(shí)長(zhǎng)一年的 C++ 個(gè)人練習(xí)生,喜歡野指針、模板報(bào)錯(cuò)和未定義行為(undefined behavior)。之前在寫(xiě)設(shè)計(jì)模式的『工廠模式』時(shí),一腳踩到了構(gòu)造、繼承和 new 組合起來(lái)的坑,現(xiàn)在也有時(shí)間來(lái)整理一下了。

構(gòu)造函數(shù)

眾所周知:在創(chuàng)建對(duì)象時(shí),防止有些成員沒(méi)有被初始化導(dǎo)致不必要的錯(cuò)誤,在創(chuàng)建對(duì)象的時(shí)候自動(dòng)調(diào)用構(gòu)造函數(shù)(無(wú)聲明類(lèi)型),完成成員的初始化。即:

Class c // 隱式,默認(rèn)構(gòu)造函數(shù)
Class c = Class() // 顯示,默認(rèn)構(gòu)造函數(shù)
Class c = Class("name") // 顯示,非默認(rèn)構(gòu)造函數(shù) 
Class* c = new Class // 隱式,默認(rèn)構(gòu)造函數(shù)
  • 構(gòu)造函數(shù)執(zhí)行前,對(duì)象不存在
  • 構(gòu)造函數(shù)創(chuàng)建對(duì)象后,對(duì)象不能調(diào)用構(gòu)造函數(shù)
  • 類(lèi)中如果不定義構(gòu)造函數(shù),編譯器提供有默認(rèn)的構(gòu)造函數(shù),無(wú)參數(shù),也不執(zhí)行任何額外的語(yǔ)句
  • 如果提供非默認(rèn)構(gòu)造函數(shù),沒(méi)有默認(rèn)構(gòu)造函數(shù)將會(huì)出錯(cuò)。所以要定義一個(gè)不接受任何參數(shù)的構(gòu)造函數(shù),并為成員定義合理的值
  • 一般而言,默認(rèn)的構(gòu)造函數(shù)是用來(lái)對(duì)所有類(lèi)成員做隱式初始化的
  • 自己定義的構(gòu)造函數(shù)一般用使用列表初始化來(lái)初始化參數(shù)
  • 通過(guò)構(gòu)造函數(shù)對(duì)成員賦值,要優(yōu)于通過(guò)函數(shù)為成員賦值
using namespace std;

class Stone {
private:
    int weight{0};
    double radius{0.0};
public:
    Stone() {
        cout << "Class Stone was created by default creator" << endl;
    };
    Stone(int w, double r) : weight{w}, radius{r} {
        cout << "Class Stone was created by custom creator" << endl;
    }
    void showInfo() {
        cout << "Weight: " << this->weight << ", Radius: " 
             << this->radius << endl;
    }
};

int main (){
    // 隱式,成員有默認(rèn)值
    Stone s1;
    s1.showInfo();
    // 顯式,通過(guò)列表初始化,為成員賦值
    Stone s2 = Stone(12, 3.3);
    s2.showInfo();
    return 0;
}

通過(guò)構(gòu)造函數(shù)實(shí)現(xiàn)的類(lèi)型轉(zhuǎn)換

觀察以下的代碼,我們發(fā)現(xiàn) Stone s2;s2 = 3.3; 這樣將一個(gè) double 類(lèi)型的數(shù)據(jù)賦值給類(lèi)類(lèi)型并沒(méi)有出錯(cuò),這是隱式類(lèi)型轉(zhuǎn)換,從參數(shù)類(lèi)型到類(lèi)類(lèi)型。

using namespace std;

class Stone {
private:
    int weight{0};
    double radius{0.0};
public:
    Stone() {
        cout << this << endl;
        cout << "Class Stone was created by default creator" << endl;
    };
    // 都關(guān)閉
    Stone(double r) : radius{r} {
        cout << this << endl;
        cout << "Class Stone was created by parameter radius" << endl;
    }
    Stone(int w) : weight{w} {
        cout << this << endl;
        cout << "Class Stone was created by parameter weight" << endl;
    }
    void showInfo() {
        cout << "Weight: " << this->weight << ", Radius: " 
             << this->radius << endl;
    }
};

int main (){
    Stone s2;
    s2 = 3.3;
    s2.showInfo();
    return 0;
}

這是因?yàn)椋航邮芤粋€(gè)參數(shù)的構(gòu)造函數(shù)允許使用賦值語(yǔ)法來(lái)為對(duì)象賦值。s2=3.3 會(huì)創(chuàng)建 Stock(double) 臨時(shí)對(duì)象,臨時(shí)對(duì)象初始化后,逐成員賦值的方式復(fù)制到對(duì)象中,在幾個(gè)構(gòu)造函數(shù)中加入了 cout << this 的語(yǔ)句,由對(duì)象的地址不同,可以判斷該賦值語(yǔ)句額外生成了臨時(shí)對(duì)象。

為了防止隱式轉(zhuǎn)換帶來(lái)的危險(xiǎn),可以使用關(guān)鍵字 explicit 關(guān)閉這一特性,這樣就得顯式完成參數(shù)類(lèi)型到類(lèi)類(lèi)型的轉(zhuǎn)換:s = Stock(1.3);不過(guò),得保證沒(méi)有二義性。

using namespace std;

class Stone {
private:
    int weight{0};
    double radius{0.0};
public:
    Stone() {
        cout << this << endl;
        cout << "Class Stone was created by default creator" << endl;
    };
    // 都關(guān)閉
    explicit Stone(double r) : radius{r} {
        cout << this << endl;
        cout << "Class Stone was created by parameter radius" << endl;
    }
    explicit Stone(int w) : weight{w} {
        cout << this << endl;
        cout << "Class Stone was created by parameter weight" << endl;
    }
    void showInfo() {
        cout << "Weight: " << this->weight << ", Radius: " 
             << this->radius << endl;
    }
};

int main (){
    Stone s2;
    s2 = Stone(3);
    s2.showInfo();
    return 0;
}

上述代碼中,如果 Stone(int w) 沒(méi)有被關(guān)閉,那么 s2=3.3 將調(diào)用這一構(gòu)造函數(shù)。所以構(gòu)造函數(shù)建議都加上 explicit 聲明。

派生類(lèi)的構(gòu)造函數(shù)

派生類(lèi)要注意的是:派生類(lèi)被構(gòu)造之前,通過(guò)調(diào)用一個(gè)基類(lèi)的構(gòu)造函數(shù),創(chuàng)建基類(lèi)完成基類(lèi)數(shù)據(jù)成員的初始化;也就是說(shuō),基類(lèi)對(duì)象在程序進(jìn)入派生類(lèi)構(gòu)造函數(shù)之前被創(chuàng)建。那么,可以通過(guò)初始化列表傳遞給基類(lèi)參數(shù),不傳遞的話,調(diào)用基類(lèi)的默認(rèn)的構(gòu)造函數(shù),如下述程序中的:Gem(){}:Stone()。

using namespace std;

class Stone {
private:
    int weight{0};
    double radius{0.0};
public:
    Stone() {
        cout << "This object was in address: " << this << endl;
    };
    Stone(int w, double r) : weight{2}, radius{r} {};
    void showInfo() {
        cout << "Weight: " << this->weight << ", Radius: " << this->radius;
    }
    int getWeight(){
        return this->weight;
    }
    auto getRadius() -> double {
        return this->radius;
    }
};

class Gem : public Stone {
private:
    double price;
public:
    Gem(){};
    Gem(double p, int w, double r) : Stone(w, r), price{p} {};
    void show() {
        cout << "Weight: " << this->getWeight() << ", Radius" 
             << this->getRadius();
    }
};

int main (){
    Gem g1; // call default
    Gem g2 = Gem(1300, 1, 2.3); // call custom 
    // g.setWeight(130);
    g2.show();
    return 0;
}

  • 首先創(chuàng)建基類(lèi)對(duì)象
  • 派生類(lèi)通過(guò)初始化列表(只能用在構(gòu)造函數(shù))將基類(lèi)信息傳遞給基類(lèi)的構(gòu)造函數(shù)
  • 派生類(lèi)構(gòu)造函數(shù)可以為派生類(lèi)初始化新的成員

析構(gòu)函數(shù)

對(duì)象過(guò)期時(shí),程序會(huì)調(diào)用對(duì)象的析構(gòu)函數(shù)完成一些清理工作,如釋放變量開(kāi)辟的空間等。如構(gòu)造函數(shù)使用了 new 來(lái)申請(qǐng)空間,析構(gòu)就需要 delete 來(lái)釋放空間。如果沒(méi)有特別聲明析構(gòu)函數(shù),編譯器會(huì)為類(lèi)提供默認(rèn)的析構(gòu)函數(shù),在對(duì)象作用域到期、被刪除時(shí)自動(dòng)被調(diào)用。

如 stock1 = Stock(),這種就申請(qǐng)了一個(gè)臨時(shí)變量,變量消失時(shí)會(huì)調(diào)用析構(gòu)函數(shù)。此外,這種局部變量放在棧區(qū),先入后出,也就是,最后被申請(qǐng)的變量最先被釋放。

using namespace std;

class Stone {
private:
    int weight{0};
    double radius{0.0};
public:
    Stone() {
        cout << "This object was in address: " << this << endl;
    };
    ~Stone() {
        cout << this << " Object was deleted." << endl;
    }
};

int main (){
    {
        Stone s1;
        Stone s2;
    }
    return 0;
}

繼承中的析構(gòu)函數(shù)

繼承類(lèi)比較容易理解,畢竟都學(xué)過(guò)面向?qū)ο?。公有繼承的時(shí)候,基類(lèi)的公有成員也是派生類(lèi)的共有成員;私有成員也是派生類(lèi)的一部分,不過(guò)需要共有或保護(hù)方法來(lái)訪問(wèn)。但是但是但是,派生類(lèi)和基類(lèi)的析構(gòu)函數(shù)之間,也是一個(gè)坑。在繼承中:

  • 如果一個(gè)方法不是虛方法,那么將根據(jù)引用類(lèi)型或指針類(lèi)型選擇執(zhí)行的方法
  • 如果一個(gè)方法是虛方法,將根據(jù)指針或引用指向?qū)ο蟮念?lèi)型選擇執(zhí)行的方法

在繼承中,對(duì)象的銷(xiāo)毀順序和創(chuàng)建相反。創(chuàng)建時(shí)先創(chuàng)建基類(lèi),而后創(chuàng)建子類(lèi);銷(xiāo)毀時(shí),先調(diào)用子類(lèi)的析構(gòu)函數(shù),而后自動(dòng)調(diào)用基類(lèi)的析構(gòu)函數(shù)。因此,對(duì)于基類(lèi)而言,建議將析構(gòu)函數(shù)寫(xiě)成虛方法。如果析構(gòu)不是虛方法,對(duì)于以下情況,只有基類(lèi)的析構(gòu)被調(diào)用;如果析構(gòu)是虛方法,子類(lèi)、基類(lèi)的析構(gòu)方法都被調(diào)用??梢試L試刪除下述代碼的 virtual 來(lái)觀察結(jié)果:

using namespace std;

class Stone {
private:
    int weight{0};
    double radius{0.0};
public:
    Stone() {
        cout << "This object was in address: " << this << endl;
    };
    Stone(int w, double r) : weight{2}, radius{r} {};
    void showInfo() {
        cout << "Weight: " << this->weight << ", Radius: " 
             << this->radius;
    }
    int getWeight(){
        return this->weight;
    }
    auto getRadius() -> double {
        return this->radius;
    }
    virtual ~Stone() {
        cout << "Stone class was deleted." << endl;
    }
};

class Gem : public Stone {
private:
    double price;
public:
    Gem() {};
    Gem(double p, int w, double r) : Stone(w, r), price{p} {};
    void show() {
        cout << "Weight: " << this->getWeight() << ", Radius" 
             << this->getRadius();
    }
    ~Gem() {
        cout << "Gem class was deleted." << endl;
    }
};

int main (){
    Stone* s1 = new Gem(2.3, 2, 3.2);
    delete s1;
    // Gem* g1 = new Gem(2.3, 2, 1.2);
    // delete g1;
    return 0;
}

應(yīng)用

大概常見(jiàn)的坑在上面都記錄好了,來(lái)看一段我寫(xiě)的危險(xiǎn)的程序(我大概抽象了一下),覆蓋了:野指針和為定義行為:

using namespace std;

class A {
private:
    int* a;
public:
    int* create() {
        a = new int();
        return a;
    }
    ~A(){
        delete a;
    }
};

int main () {
    A a;
    int* b = a.create();
    delete b;
    return 0;
}

  1. 每次調(diào)用 create 都會(huì) new 一次,但只 delete 了一次。
  2. 如果沒(méi)有調(diào)用 create 直接析構(gòu),未定義行為
  3. 如果 b 持有了 a.create() 的指針,然后 a 提前析構(gòu),那么 b 是野指針
  4. delete b 是沒(méi)必要的。這樣會(huì) double free,也是未定義行為
  5. 上述代碼沒(méi)有區(qū)分類(lèi)里面 new 且 返回的東西要在哪刪除合適
  6. 可以讓類(lèi)來(lái)管理這一個(gè) new,修改一下 create 的實(shí)現(xiàn)或者干脆在構(gòu)造 new,在析構(gòu) delete

總結(jié)

到此這篇關(guān)于C++踩坑實(shí)戰(zhàn)之構(gòu)造和析構(gòu)函數(shù)的文章就介紹到這了,更多相關(guān)C++構(gòu)造和析構(gòu)函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++實(shí)現(xiàn)截圖截屏的示例代碼

    C++實(shí)現(xiàn)截圖截屏的示例代碼

    本文主要介紹了C++實(shí)現(xiàn)截圖截屏的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • C++ 空指針解引用的解決方法

    C++ 空指針解引用的解決方法

    空指針解引用是一種常見(jiàn)且嚴(yán)重的錯(cuò)誤,它通常由于指針未初始化、被設(shè)置為nullptr或指向無(wú)效地址引起,本文主要介紹了C++ 空指針解引用的解決方法,感興趣的可以了解一下
    2024-08-08
  • Opencv光流運(yùn)動(dòng)物體追蹤詳解

    Opencv光流運(yùn)動(dòng)物體追蹤詳解

    這篇文章主要為大家詳細(xì)介紹了Opencv光流運(yùn)動(dòng)物體追蹤的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • C++類(lèi)中隱藏的幾個(gè)默認(rèn)函數(shù)你知道嗎

    C++類(lèi)中隱藏的幾個(gè)默認(rèn)函數(shù)你知道嗎

    這篇文章主要為大家詳細(xì)介紹了C++類(lèi)中隱藏的幾個(gè)默認(rèn)函數(shù),使用數(shù)據(jù)庫(kù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • tinyxml 常用的C++ XML解析器非常優(yōu)秀

    tinyxml 常用的C++ XML解析器非常優(yōu)秀

    讀取和設(shè)置xml配置文件是最常用的操作,試用了幾個(gè)C++的XML解析器,個(gè)人感覺(jué)TinyXML是使用起來(lái)最舒服的,因?yàn)樗腁PI接口和Java的十分類(lèi)似,面向?qū)ο笮院芎?/div> 2012-11-11
  • C語(yǔ)言基礎(chǔ)之二分查找知識(shí)最全匯總

    C語(yǔ)言基礎(chǔ)之二分查找知識(shí)最全匯總

    這篇文章主要介紹了C語(yǔ)言基礎(chǔ)之二分查找知識(shí)最全匯總,文中有非常詳細(xì)的二分查找基礎(chǔ)知識(shí)詳解,對(duì)正在學(xué)習(xí)C語(yǔ)言基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • 一文弄懂C語(yǔ)言EOF

    一文弄懂C語(yǔ)言EOF

    在 C語(yǔ)言中,EOF 是一個(gè)宏定義,EOF 常常用于文件的輸入輸出中,當(dāng)讀取到文件結(jié)束時(shí),會(huì)返回 EOF,本文就詳細(xì)的介紹一下具體使用方法,感興趣的可以一起來(lái)了解一下
    2023-05-05
  • C語(yǔ)言驅(qū)動(dòng)開(kāi)發(fā)之通過(guò)ReadFile與內(nèi)核層通信

    C語(yǔ)言驅(qū)動(dòng)開(kāi)發(fā)之通過(guò)ReadFile與內(nèi)核層通信

    驅(qū)動(dòng)與應(yīng)用程序的通信是非常有必要的,內(nèi)核中執(zhí)行代碼后需要將其動(dòng)態(tài)顯示給應(yīng)用層。為了實(shí)現(xiàn)內(nèi)核與應(yīng)用層數(shù)據(jù)交互則必須有通信的方法,微軟為我們提供了三種通信方式,本文先來(lái)介紹通過(guò)ReadFile系列函數(shù)實(shí)現(xiàn)的通信模式
    2022-09-09
  • 必須知道的C語(yǔ)言八大排序算法(收藏)

    必須知道的C語(yǔ)言八大排序算法(收藏)

    這篇文章主要介紹了C語(yǔ)言八大排序算法的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-10-10
  • C和C++中的基本數(shù)據(jù)類(lèi)型的大小及表示范圍詳解

    C和C++中的基本數(shù)據(jù)類(lèi)型的大小及表示范圍詳解

    這篇文章主要介紹了C和C++中的基本數(shù)據(jù)類(lèi)型的大小及表示范圍詳解,基本數(shù)據(jù)類(lèi)型有int、long、long long、float、double、char、string,正文有詳細(xì)介紹,歡迎參考
    2018-01-01

最新評(píng)論

衢州市| 齐齐哈尔市| 赤城县| 阿拉善左旗| 那曲县| 通海县| 南投县| 琼结县| 云安县| 福鼎市| 吉木乃县| 石景山区| 兴义市| 滁州市| 迁西县| 虎林市| 惠来县| 桐柏县| 罗城| 枣庄市| 和林格尔县| 阳高县| 江都市| 凤凰县| 龙口市| 临西县| 上栗县| 凉山| 井陉县| 彭阳县| 南江县| 合山市| 牡丹江市| 石城县| 连江县| 南陵县| 平山县| 淮南市| 焦作市| 长阳| 宝应县|