自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

構(gòu)造與析構(gòu):C++對象背后的生死較量

開發(fā)
C++編譯器就像是一位貼心的管家,當(dāng)你只寫了一個析構(gòu)函數(shù)時,它會默默地為你準(zhǔn)備好所有需要的"禮物" !

在C++的奇妙世界里,構(gòu)造函數(shù)和析構(gòu)函數(shù)就像是一對可愛的舞臺搭檔 - 構(gòu)造函數(shù)負(fù)責(zé)熱情地喊出"歡迎光臨!",而析構(gòu)函數(shù)則優(yōu)雅地說著"后會有期~"。它們就像是照看對象的盡職保姆 ,從出生到離別的每一刻都不離不棄,默默守護(hù)著對象的整個生命周期。這對搭檔雖然經(jīng)常"斗嘴" ,但卻配合得天衣無縫,為我們的程序演繹著最動人的代碼故事。

默認(rèn)構(gòu)造函數(shù)的神奇魔法

你知道嗎?C++編譯器就像是一位貼心的管家 ??,當(dāng)你只寫了一個析構(gòu)函數(shù)時,它會默默地為你準(zhǔn)備好所有需要的"禮物" !這些禮物包括默認(rèn)構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、移動構(gòu)造函數(shù)(C++11的新玩具 ),以及它們的賦值運算符小伙伴們。

來看看這個有趣的派對場景:

class Party {
public:
    ~Party() { /* 收拾派對現(xiàn)場 */ }  // 你只負(fù)責(zé)打掃就好

    // 以下函數(shù)由編譯器自動生成
    Party();                          // 默認(rèn)構(gòu)造函數(shù)
    Party(const Party&);              // 拷貝構(gòu)造函數(shù)
    Party(Party&&);                   // 移動構(gòu)造函數(shù)
    Party& operator=(const Party&);   // 拷貝賦值運算符
    Party& operator=(Party&&);        // 移動賦值運算符
};

// 瞧瞧管家為我們準(zhǔn)備的這些精彩玩法 ??
Party p1;                    // 開啟新派對!??
Party p2(p1);               // 復(fù)制一個一模一樣的派對 ??
Party p3 = std::move(p1);   // 把派對搬到新地方 ??
p2 = p3;                    // 把派對方案復(fù)制一份 ??
p2 = std::move(p3);         // 派對場地大轉(zhuǎn)移 ??

有趣的是,我們的管家還很節(jié)儉呢!如果你沒用到某個功能,比如從沒搬過派對場地,管家就不會為移動構(gòu)造函數(shù)操心。這就是所謂的"按需服務(wù)",多貼心啊!

默認(rèn)構(gòu)造函數(shù)的神奇魔法

你一定會好奇,為什么C++要這么貼心地幫我們準(zhǔn)備這些默認(rèn)函數(shù)呢?這就像是準(zhǔn)備一場完美派對 - 當(dāng)你說"我要收拾派對現(xiàn)場"(定義析構(gòu)函數(shù))的時候,C++就會想:"哎呀,既然要收拾,那一定是開過派對的吧!" 

所以它會自動幫你準(zhǔn)備好開派對的所有必需品(默認(rèn)構(gòu)造函數(shù)),復(fù)制派對方案的工具(拷貝構(gòu)造函數(shù)),甚至還有搬家用的箱子(移動構(gòu)造函數(shù))。這些都是為了確保我們的對象能夠快樂地誕生 、成長、搬家,最后優(yōu)雅地說再見 。

這就像是一個全套的生命服務(wù),缺一不可 。因為在C++的世界里,有始就要有終,有終就必須有始,這是一個完整的生命周期呀!

所以,盡管你只定義了析構(gòu)函數(shù),C++依然會為你生成一個默認(rèn)構(gòu)造函數(shù),確保你的Party對象能夠順利地被創(chuàng)建。就像一個無聲的英雄,默默地為你的代碼保駕護(hù)航。

總之,C++的構(gòu)造函數(shù)和析構(gòu)函數(shù)就像是派對的開場和謝幕,雖然你可能只關(guān)注了謝幕,但開場的精彩同樣不容錯過!

虛析構(gòu)函數(shù) - 繼承體系中的安全衛(wèi)士

在繼承關(guān)系中,析構(gòu)函數(shù)是否聲明為虛函數(shù)變得尤為重要。讓我們通過一個小例子來看看為什么需要虛析構(gòu)函數(shù):

class Animal {
public:
    ~Animal() { 
        std::cout << "再見,動物!" << std::endl; 
    }
};

class Dog : public Animal {
public:
    ~Dog() { 
        std::cout << "再見,小狗!" << std::endl; 
    }
};

int main() {
    Animal* pet = new Dog();  // 通過基類指針指向派生類對象 ??
    delete pet;               // 糟糕!只會調(diào)用 Animal 的析構(gòu)函數(shù) ??
}

在上面的例子中,delete pet 只會調(diào)用Animal 的析構(gòu)函數(shù),而不會調(diào)用Dog 的析構(gòu)函數(shù)。這會導(dǎo)致Dog 類中可能存在的資源沒有被釋放,從而引發(fā)內(nèi)存泄漏。

讓我們來修復(fù)這個問題:

class Animal {
public:
    virtual ~Animal() {  // 添加 virtual 關(guān)鍵字 ?
        std::cout << "再見,動物!" << std::endl; 
    }
};

class Dog : public Animal {
public:
    ~Dog() override {    // 使用 override 更清晰 ??
        std::cout << "再見,小狗!" << std::endl; 
    }
};

int main() {
    Animal* pet = new Dog();
    delete pet;  // 現(xiàn)在會正確調(diào)用 Dog 的析構(gòu)函數(shù),然后是 Animal 的析構(gòu)函數(shù) ??
}

通過將Animal 的析構(gòu)函數(shù)聲明為虛函數(shù),delete pet 會首先調(diào)用Dog 的析構(gòu)函數(shù),然后調(diào)用Animal 的析構(gòu)函數(shù),確保所有資源都被正確釋放。這樣就不會有內(nèi)存泄漏的問題啦!

為什么需要虛析構(gòu)函數(shù)? 

在繼承關(guān)系中,使用基類指針指向派生類對象時,如果基類的析構(gòu)函數(shù)不是虛函數(shù),刪除該指針時只會調(diào)用基類的析構(gòu)函數(shù),而不會調(diào)用派生類的析構(gòu)函數(shù)。這會導(dǎo)致派生類中分配的資源沒有被正確釋放,從而引發(fā)內(nèi)存泄漏。??

析構(gòu)順序的秘密

你可能會問:"為什么聲明為虛函數(shù)后,會依次調(diào)用 Dog 和 Animal 的析構(gòu)函數(shù)呢?不是已經(jīng)重寫了嗎?" 讓我們來揭開這個秘密:

class Animal {
protected:
    int* animalResource;  // 基類的資源 ???
public:
    Animal() { animalResource = new int(1); }
    virtual ~Animal() { 
        delete animalResource;
        std::cout << "再見,動物!" << std::endl; 
    }
};

class Dog : public Animal {
private:
    int* dogResource;    // 派生類的資源 ??
public:
    Dog() { dogResource = new int(2); }
    ~Dog() override { 
        delete dogResource;
        std::cout << "再見,小狗!" << std::endl; 
    }
};

這是因為在 C++ 中,派生類對象的析構(gòu)過程遵循特定的順序:

  • 首先調(diào)用派生類(Dog)的析構(gòu)函數(shù)
  • 然后自動調(diào)用基類(Animal)的析構(gòu)函數(shù)

這個過程是自動且必然的,原因如下:

(1) 內(nèi)存布局:Dog 對象不僅包含自己的成員(dogResource),還包含從 Animal 繼承來的所有成員(animalResource)

(2) 資源清理:

  • Dog 的析構(gòu)函數(shù)負(fù)責(zé)清理 Dog 特有的資源
  • Animal 的析構(gòu)函數(shù)負(fù)責(zé)清理繼承來的資源
  • 如果不調(diào)用基類的析構(gòu)函數(shù),基類的資源就會泄露

(3) 執(zhí)行順序:就像蓋房子和拆房子

  • 蓋房子時是從下往上(先構(gòu)造基類,再構(gòu)造派生類)
  • 拆房子時是從上往下(先析構(gòu)派生類,再析構(gòu)基類)

所以當(dāng)我們執(zhí)行:

Animal* pet = new Dog();
delete pet;

輸出會是:

再見,小狗!    // 先清理 Dog 的資源
再見,動物!    // 再清理 Animal 的資源

這不是普通的函數(shù)重寫,而是 C++ 特有的析構(gòu)機制,確保對象的完整清理。就像拆房子必須從頂層開始拆一樣,析構(gòu)也必須從派生類開始,層層向下進(jìn)行!

普通函數(shù)重寫 vs 析構(gòu)函數(shù)

讓我們來對比一下普通虛函數(shù)的重寫和析構(gòu)函數(shù)的區(qū)別:

class Animal {
public:
    // 普通虛函數(shù)
    virtual void speak() {
        std::cout << "動物在說話" << std::endl;
    }
    
    // 析構(gòu)函數(shù)
    virtual ~Animal() {
        std::cout << "再見,動物!" << std::endl;
    }
};

class Dog : public Animal {
public:
    // 普通函數(shù)重寫 - 只會調(diào)用這個版本
    void speak() override {
        std::cout << "汪汪汪!" << std::endl;
    }
    
    // 析構(gòu)函數(shù) - 會調(diào)用這個,然后自動調(diào)用基類版本
    ~Dog() override {
        std::cout << "再見,小狗!" << std::endl;
    }
};

int main() {
    Animal* pet = new Dog();
    
    pet->speak();    // 輸出:汪汪汪!
    delete pet;      // 輸出:再見,小狗! 再見,動物!
}
  • 普通函數(shù)重寫:完全替換基類的版本,只會執(zhí)行派生類的實現(xiàn)
  • 析構(gòu)函數(shù):是一個特殊的過程,會依次執(zhí)行派生類和基類的析構(gòu)函數(shù)

這種區(qū)別的設(shè)計是有意義的:

  • 普通函數(shù)重寫:我們希望完全替換掉基類的行為
  • 析構(gòu)函數(shù):我們需要清理整個繼承鏈上的所有資源,不能遺漏

性能考慮

添加虛析構(gòu)函數(shù)會帶來一些開銷:

  • 每個對象都會多一個虛函數(shù)表指針(vptr) 
  • 類的大小會增加(通常是一個指針的大小) 
  • 虛函數(shù)調(diào)用比普通函數(shù)調(diào)用稍慢 

但是相比于內(nèi)存泄漏的風(fēng)險,這點開銷是值得的!

最佳實踐 

  • 如果你的類將被繼承,請將析構(gòu)函數(shù)聲明為虛函數(shù) 
  • 如果你的類不會被繼承,則不需要虛析構(gòu)函數(shù)
  • 在聲明虛析構(gòu)函數(shù)時,建議使用override 關(guān)鍵字(C++11及以后) 

通過遵循這些最佳實踐,你的代碼將更加健壯,避免不必要的內(nèi)存泄漏問題。

責(zé)任編輯:趙寧寧 來源: everystep
相關(guān)推薦

2011-06-15 09:47:14

C++

2009-08-14 17:24:28

C#構(gòu)造函數(shù)和析構(gòu)函數(shù)

2025-02-18 00:08:00

代碼C++RAII

2011-07-15 01:29:39

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

2009-09-03 13:14:55

C#構(gòu)造函數(shù)C#析構(gòu)函數(shù)

2010-01-18 15:53:27

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

2010-02-04 16:39:26

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

2009-07-30 15:24:13

C#析構(gòu)函數(shù)C#構(gòu)造函數(shù)

2024-12-19 14:42:15

C++內(nèi)存泄漏內(nèi)存管理

2010-02-05 13:35:19

C++虛析構(gòu)函數(shù)

2010-07-20 09:52:27

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

2021-01-17 16:29:51

C++Python語言

2010-01-27 10:13:22

C++類對象

2024-04-28 11:01:27

C++編程語言函數(shù)

2009-09-02 10:49:46

C#調(diào)用析構(gòu)方法

2009-08-13 17:30:30

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

2011-07-20 13:40:09

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

2012-08-15 13:31:02

筆試題

2010-01-27 17:16:52

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

2015-05-25 10:52:49

點贊
收藏

51CTO技術(shù)棧公眾號