[英]When are variables removed from memory in C++?
我現在一直在使用C ++。 我只是不確定內存管理是如何工作的,所以在這里:
我首先不確定函數中的內存是如何分配的,例如:
int addTwo(int num)
{
int temp = 2;
num += temp;
return num;
}
那么在這個例子中,函數結束后是否會從內存中刪除? 如果沒有,這是怎么做的。 在C#中,一旦變量用完,變量就會被刪除。 還有其他我應該知道的案例嗎?
謝謝
在C ++中有一個非常簡單的經驗法則:
除非已手動分配,否則當超出范圍時,將自動釋放所有內存。
手動分配:
C ++中一個非常有用的設計模式稱為RAII ( 資源獲取是初始化 ),它將動態分配綁定到一個作用域對象,從而釋放其析構函數中的分配。
在RAII代碼中,您不必再擔心調用delete()或free(),因為只要“錨點對象”超出范圍,就會自動調用它們。
這里, temp
被分配在堆棧上,當函數退出時,它自動釋放它使用的內存。 但是,您可以在堆上分配它,如下所示:
int *temp = new int(2);
要解放它,你必須這樣做
delete temp;
如果在堆棧上分配變量,通常會發生這種情況:
當你調用你的函數時,它會增加這個稱為“堆棧指針”的東西 - 一個數字,說明內存中的哪些地址被“保護”以供其局部變量使用。 當函數返回時,它會將堆棧指針遞減到其原始值。 實際上沒有對你在該函數中分配的變量進行任何操作,除了它們所在的內存不再受“保護” - 其他任何東西都可以(並最終將)覆蓋它們。 所以你不應該再訪問它們了。
如果在退出函數后需要分配的內存保持不變,則使用堆。
局部變量temp
在函數開始時被“推”到堆棧上,並在函數退出時“彈出”堆棧。
這是非優化版本的反匯編:
int addTwo(int num)
{
00411380 push ebp
00411381 mov ebp,esp //Store current stack pointer
00411383 sub esp,0CCh //Reserve space on stack for locals etc
00411389 push ebx
0041138A push esi
0041138B push edi
0041138C lea edi,[ebp-0CCh]
00411392 mov ecx,33h
00411397 mov eax,0CCCCCCCCh
0041139C rep stos dword ptr es:[edi]
int temp = 2;
0041139E mov dword ptr [temp],2
num += temp;
004113A5 mov eax,dword ptr [num]
004113A8 add eax,dword ptr [temp]
004113AB mov dword ptr [num],eax
return num;
004113AE mov eax,dword ptr [num]
}
004113B1 pop edi
004113B2 pop esi
004113B3 pop ebx
004113B4 mov esp,ebp //Restore stack pointer
004113B6 pop ebp
004113B7 ret
術語“推”和“彈出”僅僅意味着類比。 從匯編輸出中可以看出,編譯器通過從堆棧指針中減去合適的值,一次性保留局部變量等的所有內存。
一旦函數退出,它就不會從內存中刪除。
它保留在內存中,在addTwo的堆棧幀中,直到某個其他進程(或相同)重新使用該部分內存。
在此之前,訪問temp是未定義的行為。
temp
在堆棧上分配。 所以當函數返回時,它就消失了。
C ++范圍規則與C#類似。
通常,內存管理用於創建的動態內存的上下文中
new
malloc
在正常的代碼中,C ++的行為與其他語言類似。 如果您創建變量或將其返回,則會在目標端復制並訪問該變量。
int a = addTwo(3);
獲取返回值的副本。 如果返回的值是一個調用的類復制操作符。 因此,只要你不使用new和malloc,你就不必那么關心內存管理了。
另外一個重要的評論
void func(std::string abc)
{
// method gets a copy of abc
}
void func(std::string& abc)
{
// method gets the original string object which can be modified without having to return it
}
void func(const std::string& abc)
{
// method gets the original string object abc but is not able to modify it
}
這三行的區別非常重要,因為您的程序可能會花費大量時間創建通常不想創建的輸入參數副本。
例如
bool CmpString(std::string a, std::string b)
{
return a.compare(b);
}
是非常昂貴的,因為字符串a和b總是被復制。 使用
bool CmpString(const std::string& a, const std::string& b)
代替。
這很重要,因為默認情況下不使用refcounted對象。
變量temp是堆棧分配的。 這意味着它在函數返回時被釋放。
見例如:
在這種情況下,num和temp都是此函數的本地。 調用該函數時,傳入num的數字將從調用者復制到堆棧上的變量。 然后在堆棧上創建Temp。 返回時,num的值被復制回調用者,並且函數中使用的temp和num變量將被刪除。
在C中,C ++ 局部變量具有自動存儲類並存儲在Stack中 。
當函數返回時,堆棧被解開並且本地不再可訪問,但它們仍然存在於內存中,這就是當你在函數中定義變量時它可能包含垃圾值的原因。
它只是堆棧中的堆棧指針操作,實際上是刪除了本地的內存。
在C ++中,任何作用域中聲明的任何對象都將在作用域退出時被刪除。 在下面的示例中,在聲明對象時調用默認構造函數,並在退出時調用析構函數,即~MyClass。
void foo() {
MyClass object;
object.makeWonders();
}
如果在函數中聲明指針,那么當范圍退出時,指針本身(32位系統的4個字節)會被回收,但是您可能使用operator new或malloc分配的內存將會延遲 - 這通常稱為內存泄漏。
void foo() {
MyClass* object = new MyClass;
object->makeWonders();
// leaking sizeof(MyClass) bytes.
}
如果你真的必須通過new分配一個對象,當它退出范圍時需要刪除,那么你應該使用boost :: scoped_ptr,如下所示:
void foo() {
boost::scoped_ptr<MyClass> object(new MyClass);
object->makeWonders();
// memory allocated by new gets automatically deleted here.
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.