2009-10-02 13 views
2

我對C++編程相對來說比較陌生,但是我是一個10年的C程序員,所以對於指向對象的指針比我對對象的引用更舒適。指針的向量在這裏是不必要的還是更糟的,會導致內存泄漏?

我正在寫紙牌遊戲 - 這種設計是否不安全?有沒有更好的辦法?

無論如何,我有一個類SolitaireGame

class SolitaireGame: 
{ 
    public: 
     SolitaireGame(int numsuits = 1); 
    private: 
     Deck * _deck; 
     vector<Card> _shoe; 
}; 

Deck被這樣定義:

class Deck: 
{ 
public: 
Deck::Deck(vector<Card>& shoe); 
~Deck(); 
int DealsLeft() const { return deals_left; } 
Card * PullCard(); 
private: 
int deals_left; 
int num_each_deal; 
deque<Card *> _cards; 
}; 

Deck構造,帶給Card對象的矢量(在鞋的引用,通常104卡)並將指向每個卡的指針推到它自己的指針指針上。

Deck::Deck(vector<Card>& shoe) 
{ 
    vector<Card>::iterator iter = shoe.begin(); 

    while(iter != shoe.end()) 
    { 
     _cards.push_front(&(*iter)); 
     iter++; 
    } 
} 

}

的鞋在SolitaireGame構造函數創建。一旦創建了這個動態創建的對象Card對象 - 然後我將這個向量的引用傳遞給構造函數。

SolitaireGame::SolitaireGame(int numsuits):_numsuits(numsuits) 
{ 
    Card * c; 
    vector<Card> _shoe; 

    for(int i = 0; i < NUM_CARDS_IN_SHOE; i++) 
    { 
     c = new Card(); 
     _shoe.push_back(*c); 
    } 

    _deck = new Deck(_shoe); 
} 

我的想法是,該鞋將是爲Card對象的實際內存和DeckColumns只是處理指向那些Card對象的容器。

+0

謝謝大家誰指出我的'vector _shoe'變量在SolitaireGame構造函數中,會隱藏'_shoe'成員變量。這實際上是一個錯字,發佈問題時(代碼不包含此變量)。我想我不會編輯它,所以每個人的答案都是有意義的。 – BeeBand 2009-10-02 10:52:21

回答

15

只需要這段代碼,就會泄漏動態創建的卡片。

Card * c; 
vector<Card> _shoe; 

for(int i = 0; i < NUM_CARDS_IN_SHOE; i++) 
{ 
    c = new Card(); 
    _shoe.push_back(*c); 
} 

_shoe.push_back(*c)添加複製Card對象的指向c到的Card S中的向量。然後,您無法刪除以前創建的原始Card

分配的NUM_CARDS_IN_SHOECards載體可以更簡單地這樣實現:

std::vector<Card> _shoe(NUM_CARDS_IN_SHOE); 

看你的卡結構,它看起來像你(或幾乎有)對象之間嚴格的所有權,以便我不不要以爲你需要動態創建你的Card

注意局部變量_shoe被遮蔽類變量_shoe。這可能不是你想要的,你傳遞給Deck構造函數將走出去的範圍在構造結束當地_shoe什麼。

如果你重新排列你SolitaireGame變量,你也許可以做這樣的事情:

class SolitaireGame: 
{ 
public: 
    SolitaireGame(int numsuits = 1); 
private: 
    vector<Card> _shoe; 
    Deck _deck; 
}; 

SolitaireGame::SolitaireGame(int numsuits) 
    : _shoe(NUM_CARDS_IN_SHOE) 
    , _deck(_shoe) 
{ 
} 

我已經從一個指針改變_deck。我使用的成員變量在類定義聲明的順序構成的事實,所以_shoe將全面之前它是作爲一個參考構造函數_deck通過構建。這樣做的好處是我無需動態分配_deck。由於沒有的new用途,我知道,我不能有任何未接來電delete如無需要明確地釋放。

你是對的,你可以在你的_deck_shoe存儲指針指向Card•不用任何內存管理問題,但請注意,你不能添加或遊戲的生命週期內消除在_shoe任何Card S的否則您將使_deck中的所有指針無效。

4

我覺得這裏有兩種錯誤:

  1. 當你做_shoe.push_back(*c);,你所創建的卡對象的副本,所以保留到C內存將永遠不會被釋放。順便說一句,你應該經常檢查每個新存在的補充刪除。你的刪除在哪裏?
  2. 在你的Deck構造函數中,你保存指向棧中的對象的指針vector<Card> _shoe;),所以一旦SolitaireGame構造函數結束,它們將被刪除,指針將失效。編輯:我看到你的班級裏有另外一個_shoe,所以沒必要聲明另一個_shoe局部變量,事實上只是不宣佈它會解決這個問題。

我希望這可以幫助你一點。

1

由於這樣的遊戲對象總是有自己的套牌,所以您應該考慮讓Deck對象在SolitairGame中成爲真實成員 - 而不僅僅是一個指針。這將使甲板對象的生命週期管理變得更加簡單。例如,你不再需要一個自定義的析構函數。請記住,STL容器包含副本。如果你寫了類似

myvector.push_back(*(new foo)); 

你有內存泄漏。

另外,存儲指向一個矢量的元素是危險的,因爲該指針(或迭代一般)可能變得無效。對於矢量來說,這是需要增長的情況。另一種方法是的std ::列表,它不斷迭代後插入,刪除無效等

另外,請記住,在C++結構和類通常會得到隱含的拷貝構造函數和賦值運算符。榮譽rule of three。要麼禁止複製和分配,要麼確保資源(包括動態分配的內存)得到妥善管理。

2

最初的想法:

  1. 在類SolitaireGame,你聲明_shoe爲:

    vector<Card> _shoe;

    ,但在構造你推堆對象,以這樣的:

    c = new Card();

    _shoe.push_back(*c);

    所以,你需要聲明它是這樣的:

    vector<Card*> _shoe;

  2. 你不初始化在構造函數的變量,如在課堂上甲板deals_left和num_each_deal。我會假設你不把代碼弄亂,但這是個好主意。

  3. Class SolitaireGame創建並擁有Deck對象。它也有一個指向SolitaireGame的Card對象的Deck。這裏的所有權不清楚 - 誰刪除​​了它們?儘管指向多個容器中的對象的指針會起作用,但它可能會使調試變得更加困難,因爲有多個刪除的範圍,刪除之後使用,泄漏等等。也許可以簡化設計。也許Deck最初擁有Card對象,當它們被移除時,它們被放入SolitaireGame的矢量中,並且不會同時存在於兩個對象中。

  4. 在SolitaireGame的構造函數中,聲明瞭另一張卡片矢量,它們在類聲明中聲明瞭一個聲明。當你將Card對象放到它上面時,它們將不會被推送到正確的向量,它將在構造函數結束時超出範圍,並且類成員將爲空。只是從構造函數中刪除它。

無論如何,我需要一杯茶。之後,我會再看一眼,看看我能否提出其他建議。

+0

謝謝你的回覆,pxb。是的你是對的,我沒有在初始文章中初始化'deals_left',以免混淆代碼。回覆。第1點,聲明'矢量 _shoe'聲明瞭一個指向對象的指針向量,但我正在做的是在通過'_shoe.push_back(* c)'執行'push_back'之前解引用指針。我認爲'c'是一個指針,我正在解除引用,所以我實際上是在推動這個對象本身。或者我誤解了? – BeeBand 2009-10-02 14:39:27

+0

是的,做* c將取消引用指針,所以你會推回到向量將是對象的副本(即一個新的對象和複製構造函數將被調用來複制內部數據)。 這是你打算做什麼?這可能相當昂貴,你會得到每個Card對象的重複,而不是指向同一個對象的指針。如果矢量在內部增長,則內存將被重新分配,並且每個對象都將被再次複製。這可能是你想要的,但對我來說似乎不尋常,並可能導致微妙的錯誤/泄漏。 – pxb 2009-10-05 11:08:39

1

這個程序會泄漏內存,想知道爲什麼?或者如何?

的push_back

千萬記得這一呼籲不要插入你提供的元素,但創造了自己使用它的一個副本。閱讀this查看詳細

所以

Card *c = new Card(); // This is on heap , Need explicit release by user 

If you change it to 

    Card c; // This is on stack, will be release with stack unwinding 

複製下面的程序並執行它,{I簡單地添加日誌記錄},嘗試與兩個選項,上面

#include<iostream> 
#include <vector> 
#include <deque> 

using namespace std; 

const int NUM_CARDS_IN_SHOE=120; 

class Card 
{ 
public: 
    Card() 
    { 
     ++ctr; 
     cout<<"C'tor callend: "<<ctr<<" , time"<<endl; 
    } 
    ~Card() 
    { 
     ++dtr; 
     cout<<"D'tor called"<<dtr<<" , time, num still to release: "<<((ctr+cpy)-dtr)<<endl; 
    } 
    Card& operator=(const Card & rObj) 
    { 
     return *this; 
    } 

    Card (const Card& rObj) 
    { 
     ++cpy; 
     cout<<"Cpy'tor called"<<cpy<<endl; 
    } 
private: 


    static int ctr,dtr,rest,cpy; 
}; 
int Card::ctr; 
int Card::dtr; 
int Card::rest; 
int Card::cpy; 
class Deck 
{ 
public: 
Deck::Deck(vector<Card>& shoe); 
~Deck(); 
int DealsLeft() const { return deals_left; } 
Card * PullCard(); 
private: 
int deals_left; 
int num_each_deal; 
std::deque<Card *> _cards; 
}; 

Deck::Deck(vector<Card>& shoe) 
{ 
    vector<Card>::iterator iter = shoe.begin(); 

    while(iter != shoe.end()) 
    { 
     _cards.push_front(&(*iter)); 
     iter++; 
    } 
} 
class SolitaireGame 
{ 
    public: 
     SolitaireGame(int numsuits = 1); 
    private: 
     Deck * _deck; 
     std::vector<Card> _shoe; 
}; 





SolitaireGame::SolitaireGame(int numsuits) 
{ 
    Card * c; 
    vector<Card> _shoe; 

    for(int i = 0; i < numsuits; i++) 
    { 
     c = new Card(); 
     _shoe.push_back(*c); 
    } 

    _deck = new Deck(_shoe); 
} 



int main() 

{ 
    { 
    SolitaireGame obj(10); 
    } 
    int a; 
    cin>>a; 
    return 0; 
} 
+0

謝謝你的回覆SSS。這是一個很棒的演示,關於'push_back'如何創建副本(在發佈這個問題之前我沒有任何想法!) - 然而,在一個相切點上,我現在完全混淆了爲什麼複製構造函數被調用35時間而不是10!我目前正在調查...... – BeeBand 2009-10-02 15:25:08

+0

@BeeBand,我沒有正確地測試過每件事情,我很遺憾,如果我犯了任何錯誤的事情,我的主要目標是讓push_back概念清楚地告訴你 – Satbir 2009-10-02 15:33:50

2

我不認爲新的關鍵字應該出現在這些類的代碼中的任何地方,我不明白爲什麼要通過指針分享卡片。存儲矢量中的項目地址是災難的祕訣 - 您需要確保在獲取地址後,不會修改矢量,因爲它往往會在內存中移動內容而不會告訴您。

假設一個Card對象不存儲除一個或兩個整數之外的任何內容,那麼處理副本和值將會簡單得多。

_deck = new Deck(_shoe); 

同樣,我沒有看到絲毫理由通過分配含有兩個整數和一個雙端隊列動態地一個目的是增加該方案的複雜性。

如果您擔心複製某些較大類的成本(我估計它對此處的感知性能沒有任何影響),那麼請不要複製它們,然後通過const參考(如果你不需要改變實例),否則就是非const引用/指針。

相關問題