2013-03-16 115 views
4

我對這個主題的其他一些問題有很好的看法,但他們都沒有(據我所知)解決了如何正確地從stl中刪除項目包含動態分配內存的對象列表與不包含動態分配內存的stl列表對象列表。STL容器與內存管理 - 對象列表與對象指針列表

我想使用一個對象列表。拿這個對象,例如(不含動態分配的內存):

class MyPoint { 

public: 
    MyPoint(int _x,int _y) 
    { 
     x = _x; 
     y = _y; 
    } 

private: 
    int x; 
    int y; 

}; 

所以我可能會創建對象的列表(不是指針他們),添加的東西給它,然後刪除元素:

list<MyPoint> myList; 

myList.push_back(MyPoint(3,4)); 
myList.push_back(MyPoint(1,2)); 
myList.push_back(MyPoint(8,8)); 
myList.push_back(MyPoint(-1,2)); 

list<MyPoint>::iterator it; 

it = myList.begin(); 
advance(it,2); 
myList.erase(it); 

我的列表現在包含: (3,4) (1,2) (-1,2)

  • 問題1A:我需要做什麼Ë是否要擦除被擦掉的物體,還是要照顧記憶?

  • 問題1b:如果程序結束,是否需要對列表中剩餘的對象做些什麼?我是否需要刪除它們並以某種方式處理它們的記憶?

好的,現在考慮允許N維空間中的一個點的類的替代版本。也就是說,我可以動態地分配一個長度爲N的數組來保存類中的N個點(我已經省去了這個實現,因爲這裏沒有問題)。然後,該類的析構函數將使用'delete'刪除動態分配的數組。

class MyDynamicPoint { 

public: 
    MyDynamicPoint(int N) 
    { 
     points = new int[N]; 
    } 

    ~MyDynamicPoint() 
    { 
     delete points; 
     points = NULL; 
    } 

private: 
    int *points; 
}; 

我現在可能會創建的指針列表的對象,而不是對象本身:

list<MyDynamicPoint*> myList; 

myList.push_back(new MyDynamicPoint(8)); 
myList.push_back(new MyDynamicPoint(10)); 
myList.push_back(new MyDynamicPoint(2)); 
myList.push_back(new MyDynamicPoint(50)); 

list<MyDynamicPoint*>::iterator it; 

it = myList.begin(); 
advance(it,2); 
myList.erase(it); 
  • 問題2a - 就是上面是否正確?即因爲這個新版本的類將包含一些動態分配的內存,這是否意味着我必須創建一個指向對象的指針列表,而不是對象本身?

  • 問題2b - 鑑於我剛剛從列表中刪除了指針,我在哪裏調用delete來處理事實,現在有動態內存要刪除的對象中?或者stl list的擦除方法調用對象的析構函數,照顧它?

提前爲任何幫助非常感謝,

最佳,

亞當

回答

0

第1個問題,有什麼你需要做的。當您按值存儲對象時,編譯器和庫將處理所有內容。

但是,當您在第二種情況下存儲指針時,您需要delete那些已分配給new的指針,否則您將發生內存泄漏。

而且你必須之前刪除指針做擦除,因爲這可以無效的迭代器:當你有一個包含了自動存儲時間的數據成員類

delete *it; 
myList.erase(it); 
4

(即其壽命是捆綁到這個類的實例)這樣的:

class MyPoint { 
private: 
    int x; 
    int y; 
}; 

,你會使用list<MyPoint> myList;,那麼std::list這種情況下還具有自動存儲durat對象離子,它會自動清理,並在容器被破壞時,它所保存的元素也會自動清理。 一切都照顧好。

但後者的版本還不是很幸運的選擇......不但你有一個容器保持指針,你甚至決定創建Point類,將被動態分配的數據成員。首先請注意,撥打new所分配的所有內容應通過調用delete來釋放,撥打new[]的所有內容都應通過撥打delete[]來釋放。

在這種情況下,你是分配對象時構建的存儲和清除它當對象被破壞:

MyDynamicPoint(int N) 
{ 
    points = new int[N]; 
} 
~MyDynamicPoint() 
{ 
    delete[] points; 
    points = NULL; 
} 
private: 
int *points; 

你應該使用一些std::vectorstd::array而不是C++中實現相同的風格的數組,你不會有照顧內存管理你自己:

MyDynamicPoint(int N) : points(std::vector<int>(N, 0)) { } 

private: 
std::vector<int> points; 

std::vector對象會照顧內存管理的爲您服務。

而最後一件事:當你動態分配的元素,並將其存儲到容器:

myList.push_back(new MyDynamicPoint(8)); 

你需要釋放你自己的這種記憶,刪除列表中的指針是不夠的:

list<MyDynamicPoint*>::iterator it; 
... 
delete *it; 
myList.erase(it); 

所以,你想實現什麼,總是喜歡用自動存儲時間的對象,如果情況允許它。沒有什麼比被迫手動處理內存管理和處理稍後發生內存泄漏等令人不快的問題更糟了。

3

問題1a:我是否需要對已擦除物體做任何其他操作,或者是否需要照顧內存?

你不需要做任何事情。

問題1b:如果程序結束,是否需要對列表中剩餘的對象做些什麼?我是否需要刪除它們並以某種方式處理它們的記憶?

你不需要做任何事情。

問題2a - 就是上面是否正確?

的代碼是不正確的。您違反了The Rule of Three。特別是,自動生成的MyDynamicPoint的拷貝構造函數和賦值操作符將對指針進行按位拷貝。如果複製的MyDynamicPoint一個實例,你就會有兩個對象共享相同的points指針結束:

  • 當一個對象去的範圍,其他變得不可用。
  • 當第二個對象超出作用域時,其析構函數將嘗試釋放已釋放的內存。這是undefined behaviour

I.e.因爲這個新版本的類將包含一些動態分配的內存,這是否意味着我必須創建一個指向對象的指針列表,而不是對象本身?

不,這並不意味着。實際上,您應該繼續按值存儲對象。但是,您需要修正三條規則。

問題2b - 考慮到我剛剛從列表中刪除了指針,我在哪裏調用delete來處理事實,現在有動態內存在對象中被刪除?或者stl list的擦除方法調用對象的析構函數,照顧它?

由於您有一個原始指針列表,因此不會自動調用析構函數。解決這個問題的最簡單方法是按值存儲對象,或者使用std::unique_ptrstd::shared_ptr而不是原始指針。

+0

* 「一旦你解決你的類將被照顧的」 * - 不,不會。他在第二個版本中存儲指向動態分配對象的指針。 – 2013-03-16 14:55:48

+0

@BenjaminLindley:你是對的,我忽略了這一點。讓我擴大答案。 – NPE 2013-03-16 14:57:19

+1

我沒有看到一個清楚的理由來贊同'std :: shared_ptr'而不是'std :: unique_ptr'。我認爲在缺少更多信息的情況下,應該使用後者。 – juanchopanza 2013-03-16 15:00:51

0

我認爲以下應工作

MyPoint* ptr = myList.back(); 

delete ptr; 

myList.pop_back(); 

OR

MyPoint* ptr = myList.back(); 

delete ptr; 

myList.erase(ptr);