問題描述
假設我定義了一些類:
class Pixel {
public:
Pixel(){ x=0; y=0;};
int x;
int y;
}
然后使用它編寫一些代碼.我為什么要執行以下操作?
Then write some code using it. Why would I do the following?
Pixel p;
p.x = 2;
p.y = 5;
來自 Java 世界,我一直在寫:
Coming from a Java world I always write:
Pixel* p = new Pixel();
p->x = 2;
p->y = 5;
他們基本上做同樣的事情,對吧?一個在堆棧上,而另一個在堆上,所以我稍后必須刪除它.兩者之間有什么根本區別嗎?為什么我應該更喜歡一個?
They basically do the same thing, right? One is on the stack while the other is on the heap, so I'll have to delete it later on. Is there any fundamental difference between the two? Why should I prefer one over the other?
推薦答案
是的,一個在堆棧上,另一個在堆上.有兩個重要區別:
Yes, one is on the stack, the other on the heap. There are two important differences:
- 首先,顯而易見但不太重要的一點是:堆分配很慢.堆棧分配很快.
- 第二,更重要的是RAII.因為堆棧分配的版本會自動清理,所以很有用.它的析構函數會被自動調用,這使您可以保證該類分配的任何資源都得到清理.這基本上是您在 C++ 中避免內存泄漏的方法.您可以通過從不自己調用
delete
來避免它們,而是將其包裝在堆棧分配的對象中,這些對象在內部調用delete
,通常在它們的析構函數中.如果您嘗試手動跟蹤所有分配,并在正確的時間調用delete
,我向您保證每 100 行代碼至少會發生內存泄漏.
- First, the obvious, and less important one: Heap allocations are slow. Stack allocations are fast.
- Second, and much more important is RAII. Because the stack-allocated version is automatically cleaned up, it is useful. Its destructor is automatically called, which allows you to guarantee that any resources allocated by the class get cleaned up. This is essentialy how you avoid memory leaks in C++. You avoid them by never calling
delete
yourself, instead wrapping it in stack-allocated objects which calldelete
internally, typicaly in their destructor. If you attempt to manually keep track of all allocations, and calldelete
at the right times, I guarantee you that you'll have at least a memory leak per 100 lines of code.
作為一個小例子,考慮以下代碼:
As a small example, consider this code:
class Pixel {
public:
Pixel(){ x=0; y=0;};
int x;
int y;
};
void foo() {
Pixel* p = new Pixel();
p->x = 2;
p->y = 5;
bar();
delete p;
}
很無辜的代碼,對吧?我們創建一個像素,然后調用一些不相關的函數,然后刪除該像素.是否存在內存泄漏?
Pretty innocent code, right? We create a pixel, then we call some unrelated function, and then we delete the pixel. Is there a memory leak?
答案是可能".如果 bar
拋出異常會發生什么?delete
永遠不會被調用,像素永遠不會被刪除,我們會泄漏內存.現在考慮這個:
And the answer is "possibly". What happens if bar
throws an exception? delete
never gets called, the pixel is never deleted, and we leak memory. Now consider this:
void foo() {
Pixel p;
p.x = 2;
p.y = 5;
bar();
}
這不會泄漏內存.當然,在這個簡單的例子中,一切都在堆棧上,所以它會自動清理,但即使 Pixel
類在內部進行了動態分配,也不會泄漏.Pixel
類將簡單地被賦予一個析構函數來刪除它,無論我們如何離開 foo
函數,都會調用這個析構函數.即使我們因為 bar
拋出異常而離開它.以下稍微做作的示例顯示了這一點:
This won't leak memory. Of course in this simple case, everything is on the stack, so it gets cleaned up automatically, but even if the Pixel
class had made a dynamic allocation internally, that wouldn't leak either. The Pixel
class would simply be given a destructor that deletes it, and this destructor would be called no matter how we leave the foo
function. Even if we leave it because bar
threw an exception. The following, slightly contrived example shows this:
class Pixel {
public:
Pixel(){ x=new int(0); y=new int(0);};
int* x;
int* y;
~Pixel() {
delete x;
delete y;
}
};
void foo() {
Pixel p;
*p.x = 2;
*p.y = 5;
bar();
}
Pixel 類現在在內部分配了一些堆內存,但是它的析構函數負責清理它,所以當使用這個類時,我們不必擔心它.(我應該提一下,這里的最后一個例子被簡化了很多,以顯示一般原則.如果我們實際使用這個類,它也包含幾個可能的錯誤.如果 y 的分配失敗,x 永遠不會被釋放, 如果 Pixel 被復制,我們最終會導致兩個實例都試圖刪除相同的數據.因此,請保留最后一個示例.實際代碼有點棘手,但它顯示了總體思路)
The Pixel class now internally allocates some heap memory, but its destructor takes care of cleaning it up, so when using the class, we don't have to worry about it. (I should probably mention that the last example here is simplified a lot, in order to show the general principle. If we were to actually use this class, it contains several possible errors too. If the allocation of y fails, x never gets freed, and if the Pixel gets copied, we end up with both instances trying to delete the same data. So take the final example here with a grain of salt. Real-world code is a bit trickier, but it shows the general idea)
當然,相同的技術可以擴展到內存分配以外的其他資源.例如,它可用于保證文件或數據庫連接在使用后關閉,或者釋放線程代碼的同步鎖.
Of course the same technique can be extended to other resources than memory allocations. For example it can be used to guarantee that files or database connections are closed after use, or that synchronization locks for your threading code are released.
這篇關于為什么不在 C++ 中對所有內容都使用指針?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!