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

C++學(xué)習(xí)之如何進(jìn)行內(nèi)存資源管理

 更新時(shí)間:2023年05月21日 10:42:09   作者:會(huì)玩code  
與java、golang等自帶垃圾回收機(jī)制的語(yǔ)言不同,C++并不會(huì)自動(dòng)回收內(nèi)存,這往往會(huì)導(dǎo)致內(nèi)存泄漏和內(nèi)存溢出等問(wèn)題,所以掌握C++中的內(nèi)存管理技巧和工具是非常重要的,本文就來(lái)和大家詳細(xì)講講

前言

與java、golang等自帶垃圾回收機(jī)制的語(yǔ)言不同,C++并不會(huì)自動(dòng)回收內(nèi)存。我們必須手動(dòng)管理堆上內(nèi)存分配和釋放,這往往會(huì)導(dǎo)致內(nèi)存泄漏和內(nèi)存溢出等問(wèn)題。而且,這些問(wèn)題可能不會(huì)立即出現(xiàn),而是運(yùn)行一段時(shí)間后,才會(huì)暴露出現(xiàn),排查也很困難。因此,了解和掌握C++中的內(nèi)存管理技巧和工具是非常重要的,可以提高程序性能、減少錯(cuò)誤和增加安全性。

內(nèi)存分區(qū)

在C++中,將操作系統(tǒng)分配給程序的內(nèi)存空間按照用途劃分了代碼段、數(shù)據(jù)段、棧、堆幾個(gè)不同的區(qū)域,每個(gè)區(qū)域都有其獨(dú)特的內(nèi)存管理機(jī)制。

代碼區(qū)

代碼區(qū)是用于存儲(chǔ)程序代碼的區(qū)域,代碼段在程序真正執(zhí)行前就被加載到內(nèi)存中,在程序執(zhí)行期間,代碼區(qū)內(nèi)存不會(huì)被修改和釋放。

由于代碼區(qū)是只讀的,所以會(huì)被多個(gè)進(jìn)程共享。在多個(gè)進(jìn)程同時(shí)執(zhí)行同一個(gè)程序時(shí),操作系統(tǒng)只需要將代碼段加載到內(nèi)存中一次,然后讓多個(gè)進(jìn)程共享這個(gè)內(nèi)存區(qū)域即可。

數(shù)據(jù)段

數(shù)據(jù)段用于存儲(chǔ)靜態(tài)全局變量、靜態(tài)局部變量和靜態(tài)常量等靜態(tài)數(shù)據(jù)。在程序運(yùn)行期間,數(shù)據(jù)段的大小固定不變,但其內(nèi)容可以被修改。按照變量是否被初始化。數(shù)據(jù)段可分為已初始化數(shù)據(jù)段和未初始化數(shù)據(jù)段。

C++中函數(shù)調(diào)用以及函數(shù)內(nèi)的局部變量的使用,都是通過(guò)棧這個(gè)內(nèi)存分區(qū)實(shí)現(xiàn)的。棧分區(qū)由操作系統(tǒng)自動(dòng)分配和釋放,是一種"后進(jìn)先出"的一種內(nèi)存分區(qū)。每個(gè)棧的大小是固定的,一般只有幾MB,所以如果棧變量太大,或者函數(shù)調(diào)用嵌套太深,容易發(fā)生棧溢出(stack overflow)。

先來(lái)一段示例代碼,看看C++是如何使用棧進(jìn)行使用棧來(lái)進(jìn)行函數(shù)調(diào)用的。

#include?<iostream>

void?inner(int?a)?{
????std::cout?<<?a?<<?std::endl;
}
void?outer(int?n)?{
?int?a?=?n?+?1;
????inner(a);
}

int?main()?{
????outer(4);
}

上面這段代碼運(yùn)行過(guò)程中的棧變化如下圖

每當(dāng)程序調(diào)用一個(gè)函數(shù)時(shí),該函數(shù)的參數(shù)、局部變量和返回地址等信息會(huì)被壓入棧中。當(dāng)函數(shù)執(zhí)行完畢,再將這些信息從棧中彈出。根據(jù)之前壓入的外層調(diào)用者壓入棧的返回地址,返回到外層調(diào)用者未執(zhí)行的代碼繼續(xù)執(zhí)行。

本地變量是直接存儲(chǔ)在棧上的,當(dāng)函數(shù)執(zhí)行完成后,這些變量占用的內(nèi)存就會(huì)被釋放掉了。前面例子中的本地變量是簡(jiǎn)單類(lèi)型,在C++中稱(chēng)為POD類(lèi)型。對(duì)于帶有構(gòu)造和析構(gòu)函數(shù)的非POD類(lèi)型變量,棧上的內(nèi)存分配同樣有效。編譯器會(huì)在合適的時(shí)機(jī),插入對(duì)構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用。

這里有個(gè)問(wèn)題,當(dāng)函數(shù)執(zhí)行發(fā)生異常時(shí),析構(gòu)函數(shù)還會(huì)被調(diào)用嗎?答案是會(huì)的,C++對(duì)于發(fā)生異常時(shí)對(duì)析構(gòu)函數(shù)的調(diào)用稱(chēng)為"棧展開(kāi)"。通過(guò)下面這段代碼演示棧展開(kāi)。

#include?<iostream>
#include?<string>

class?Obj?{
public:
????std::string?name_;
????Obj(const?std::string&?name):name_(name){std::cout?<<?"Obj()?"?<<?name_?<<?std::endl;};
????~Obj()?{std::cout?<<?"~Obj()?"?<<?name_?<<?std::endl;};
};


void?bar()?{
????auto?o?=?Obj{"bar"};
????throw?"bar?exception";
}

int?main()?{
????try?{
????????bar();
????}?catch?(const?char*?e)?{
????????std::cout?<<?"catch?Exception:?"?<<?e?<<?std::endl;
????}
}

執(zhí)行代碼的結(jié)果是:

Obj() bar
~Obj() bar
catch Exception: bar exception

可以發(fā)現(xiàn),發(fā)生異常時(shí),bar函數(shù)中的本地變量o還是能被正常析構(gòu)。

棧展開(kāi)的過(guò)程實(shí)際上是異常發(fā)生時(shí),匹配catch子句的過(guò)程。

  • 程序拋出異常,停止當(dāng)前執(zhí)行的調(diào)用鏈,開(kāi)始尋找與異常匹配的catch子句。
  • 如果異常發(fā)生在try中,則會(huì)首先檢查與該try塊匹配的catch子句。如果異常所在函數(shù)體沒(méi)有try捕獲異常。則會(huì)直接進(jìn)入下一步。
  • 如果第二步未找到匹配的catch,則會(huì)在外層的try塊中查找,直到找到為止。
  • 如果到了最外層還沒(méi)有找到匹配的catch,也就是說(shuō)異常得不到處理,程序會(huì)調(diào)用標(biāo)準(zhǔn)庫(kù)函數(shù)terminate終止函數(shù)的執(zhí)行。

在這期間,棧上所有的對(duì)象都會(huì)被自動(dòng)析構(gòu)。

堆是C++中用來(lái)存儲(chǔ)動(dòng)態(tài)分配內(nèi)存的內(nèi)存分區(qū),堆內(nèi)存的分配和釋放需要手動(dòng)管理,可以通過(guò)new/delete或malloc/free等函數(shù)進(jìn)行分配和釋放。堆內(nèi)存的大小通常是不固定的,當(dāng)我們需要?jiǎng)討B(tài)分配內(nèi)存時(shí),就可以使用堆內(nèi)存。

堆內(nèi)存由程序員手動(dòng)分配和釋放,因此使用堆內(nèi)存需要注意內(nèi)存泄漏和內(nèi)存溢出等問(wèn)題。當(dāng)程序員忘記釋放已分配的內(nèi)存時(shí),會(huì)導(dǎo)致內(nèi)存泄漏問(wèn)題。而當(dāng)申請(qǐng)的堆內(nèi)存超過(guò)了操作系統(tǒng)所分配給進(jìn)程的內(nèi)存限制時(shí),會(huì)導(dǎo)致內(nèi)存溢出問(wèn)題。

C++程序絕大多數(shù)的內(nèi)存泄露,都是由于忘記調(diào)用delete/free來(lái)釋放堆上的資源。

還是上代碼

#include?<iostream>
#include?<string>

class?Obj?{
public:
????std::string?name_;
????Obj(const?std::string&?name):name_(name){std::cout?<<?"Obj()?"?<<?name_?<<?std::endl;};
????~Obj()?{std::cout?<<?"~Obj()?"?<<?name_?<<?std::endl;};
};

Obj*?makeObj()?{
?Obj*?obj?=?nullptr;
?try?{
??obj?=?new?Obj{"makeObj"};
??...
?}?catch(...)?{
??delete?obj;
??throw;
?}
?return?obj;
}

Obj*?foo()?{
?Obj*?obj?=?nullptr;
?try?{
??obj?=?makeObj();
??...
?}?catch(...)?{
??delete?obj;
?}
?return?obj;
}
int?main()?{
????Obj*?obj?=?foo();
????...
????delete?obj;
}

可以看到,由makeObj函數(shù)創(chuàng)建的堆變量obj, 在每個(gè)獲取該變量的上層調(diào)用中,都需要關(guān)心對(duì)該變量的處理。這無(wú)疑極大得增加了開(kāi)發(fā)者的心智負(fù)擔(dān)。

RAII

想在堆上創(chuàng)建對(duì)象,又不想處理這么復(fù)雜的內(nèi)存釋放操作。C++沒(méi)有像java、golang其他語(yǔ)言創(chuàng)建一套垃圾回收機(jī)制,而是采用了一種特有的資源管理方式 --- RAII(Resource Acquisition Is Initialization,資源獲取即初始化)。

RAII利用棧對(duì)象在作用域結(jié)束后會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)的特點(diǎn),通過(guò)創(chuàng)建棧對(duì)象來(lái)管理資源。在棧對(duì)象構(gòu)造函數(shù)中獲取資源,在棧對(duì)象析構(gòu)函數(shù)中負(fù)責(zé)釋放資源,以此保證資源的獲取和釋放。

下面給出一個(gè)通過(guò)RAII來(lái)自動(dòng)釋放堆內(nèi)存的例子

#include?<iostream>

class?AutoIntPtr?{
public:
????AutoIntPtr(int*?p?=?nullptr)?:?ptr(p)?{}
????~AutoIntPtr()?{?delete?ptr;?}

????int&?operator*()?const?{?return?*ptr;?}
????int*?operator->()?const?{?return?ptr;?}

private:
????int*?ptr;
};

void?foo()?{
?AutoIntPtr?p(new?int(5));
????std::cout?<<?*p?<<?std::endl;?//?5
}

int?main()?{
????foo();
}

上面例子中,AutoIntPtr類(lèi)封裝了一個(gè)動(dòng)態(tài)分配的int類(lèi)型的指針,它的構(gòu)造函數(shù)用于獲取資源(ptr = p),析構(gòu)函數(shù)用于釋放資源(delete ptr)。當(dāng)AutoIntPtr超出作用域時(shí),自動(dòng)調(diào)用析構(gòu)函數(shù)來(lái)釋放所包含的資源。

基于RAII,C++11引入了std::unique_ptrstd::shared_ptr等智能指針用于內(nèi)存管理類(lèi),使得內(nèi)存管理變得更加方便和安全。這些內(nèi)存管理類(lèi)可以自動(dòng)進(jìn)行內(nèi)存釋放,避免了手動(dòng)釋放內(nèi)存的繁瑣工作。值得一提的是,上面的AutoIntPtr就是一個(gè)簡(jiǎn)化版的智能指針了。

在實(shí)際開(kāi)發(fā)中,RAII的應(yīng)用很廣。不僅僅用于自動(dòng)釋放內(nèi)存。還可以用來(lái)關(guān)閉文件、釋放數(shù)據(jù)庫(kù)連接、釋放同步鎖等。

總結(jié)

本文介紹了C++中的內(nèi)存管理機(jī)制,包括內(nèi)存分區(qū)、棧、堆和RAII技術(shù)等內(nèi)容。通過(guò)學(xué)習(xí)本文,我們可以更好地掌握C++的內(nèi)存管理技巧,避免內(nèi)存泄漏和內(nèi)存溢出等問(wèn)題。

以上就是C++學(xué)習(xí)之如何進(jìn)行內(nèi)存資源管理的詳細(xì)內(nèi)容,更多關(guān)于C++內(nèi)存資源管理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 淺析stl序列容器(map和set)的仿函數(shù)排序

    淺析stl序列容器(map和set)的仿函數(shù)排序

    有序的stl容器在工程中應(yīng)用什么方便和廣泛,但是當(dāng)我們需要自己的排序的時(shí)候,可以用仿函數(shù)來(lái)設(shè)置它
    2013-09-09
  • C++ 中循環(huán)鏈表和約瑟夫環(huán)

    C++ 中循環(huán)鏈表和約瑟夫環(huán)

    這篇文章主要介紹了C++ 中循環(huán)鏈表和約瑟夫環(huán)的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • C++中求余運(yùn)算符(%)示例詳解

    C++中求余運(yùn)算符(%)示例詳解

    求余運(yùn)算符“%”,二元運(yùn)算符,具有左結(jié)合性。參與運(yùn)算的量均為整型。求余運(yùn)算的結(jié)果等于兩個(gè)數(shù)相除后的余數(shù)??此坪芎?jiǎn)單的運(yùn)算符,卻也真要掌握用好它也不容易,這篇文章主要介紹了C++中求余運(yùn)算符(%)的相關(guān)資料,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    2017-01-01
  • 詳解C語(yǔ)言中rand函數(shù)的使用

    詳解C語(yǔ)言中rand函數(shù)的使用

    在編程時(shí)我們有時(shí)總希望自己產(chǎn)生一個(gè)隨機(jī)數(shù)字,以供使用,那么下面介紹rand函數(shù)的使用,有需要的可以參考學(xué)習(xí)。
    2016-08-08
  • C++特性之智能指針shared_ptr詳解

    C++特性之智能指針shared_ptr詳解

    shared_ptr是C++11提供的一種智能指針類(lèi),它足夠智能,可以在任何地方都不使用時(shí)自動(dòng)刪除相關(guān)指針,從而幫助徹底消除內(nèi)存泄漏和懸空指針的問(wèn)題。本文主要是來(lái)和大家聊聊shared_ptr的使用,需要的可以參考一下
    2022-12-12
  • 浮點(diǎn)數(shù)乘法和整形乘除法的效率經(jīng)驗(yàn)比較

    浮點(diǎn)數(shù)乘法和整形乘除法的效率經(jīng)驗(yàn)比較

    這篇文章主要為大家介紹了浮點(diǎn)數(shù)乘法和整形乘除法的效率經(jīng)驗(yàn)比較,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • C++實(shí)現(xiàn)商店倉(cāng)庫(kù)管理系統(tǒng)

    C++實(shí)現(xiàn)商店倉(cāng)庫(kù)管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)商店倉(cāng)庫(kù)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • C語(yǔ)言計(jì)算日期差的方法示例

    C語(yǔ)言計(jì)算日期差的方法示例

    這篇文章主要介紹了C語(yǔ)言計(jì)算日期差的方法,結(jié)合具體實(shí)例形式分析了C語(yǔ)言針對(duì)日期轉(zhuǎn)換、運(yùn)算等相關(guān)操作技巧,需要的朋友可以參考下
    2017-06-06
  • C語(yǔ)言經(jīng)典指針筆試題詳解

    C語(yǔ)言經(jīng)典指針筆試題詳解

    今天博主來(lái)講解4道經(jīng)典的指針筆試題,很多朋友沒(méi)有深刻理解函數(shù)傳參知識(shí)都會(huì)在這些題目上出錯(cuò),下面話不多說(shuō),我們開(kāi)始
    2021-10-10
  • C++實(shí)現(xiàn)中值濾波的示例代碼

    C++實(shí)現(xiàn)中值濾波的示例代碼

    本文主要介紹了C++實(shí)現(xiàn)中值濾波的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05

最新評(píng)論