2012-02-04 2 views
0

У меня есть классы Deck and PlayingCard. Объект Deck должен иметь динамически выделяемый массив указателей на объекты игральных карт:Освобождение памяти для массива указателей?

PlayingCard** _playing_cards; 

Чтобы инициализировать этот массив, конструктор палубы и построить() функции называются:

Deck::Deck(int size) 
{ 
    _total_playing_cards = size; 
    _deal_next = 0; 
    build(); 
} 

void Deck::build() 
{ 
    _playing_cards = new PlayingCard*[_total_playing_cards]; 
    for(int i = 1; i <= _total_playing_cards; ++i) 
    { 
     _playing_cards[i-1] = new PlayingCard(i % 13, i % 4); 
    } 
} 

Освобождение памяти, выделенная с «новый» обрабатывается в деструкторе:

Deck::~Deck() 
{ 
    for(int i = 0; i < _total_playing_cards; ++i) 
    { 
     delete[] _playing_cards[i]; 
    } 
    delete[] _playing_cards; 
} 

Я тогда отдельный файл, deck_test.cpp, который имеет основной(), чтобы просто построить и уничтожить объект Deck :

int main() 
{ 
    Deck deck(52); 
    deck.~Deck(); 
    return 0; 
} 

Это компилируется нормально, но при отладке, отчетов Visual Studio "необработанное исключение в 0x5ab159da (msvcr100d.dll) в игре Cards.exe: нарушение прав доступа чтения расположение 0xfeeefee2: 0xC0000005." При просмотре стека вызовов возникает проблема, когда я использую оператор delete [] в цикле «for» внутри деструктора. Разве это не правильный способ освободить память из массива указателей?

+0

В цикле for не было бы удаления с отсутствием [], потому что вы удаляете пространство и объект, на которые указывает _playing_cards [i]? – octopusgrabbus

ответ

0

Это из-за двух вещей:

  1. вызове деструктора вручную, а затем деструктор вызывается снова, когда переменная выходит из области видимости. Обычно вы не должны вызывать деструктор вручную, если только объект не был назначен местом размещения new, или вы скрытно восстанавливаете объект с размещением new непосредственно перед его выходом из области действия (но не делайте этого).

  2. delete[] _playing_cards[i]; должен быть delete _playing_cards[i], потому что playing_cards[i] не является массивом, это просто new PlayingCard.

Кроме того, почему вы делаете i = 1; i <= _total_playing_cards в одном месте и i = 0; i < _total_playing_cards в другой? Это бесполезно усложняет вещи, я бы посоветовал собрать один (желательно последний) и придерживаться его.

1

Пожалуйста, не вызывайте деструктор непосредственно в main().

Слегка изменить код деструктора:

Deck::~Deck() 
{ 
    if (_playing_cards) { 
     for (std::size_t i = 0; i < _total_playing_cards; ++i) { 
      delete _playing_cards[i]; 
      _playing_cards[i] = NULL; 
     } 
     delete[] _playing_cards; 
     _playing_cards = NULL; 
    } 
} 

Кстати, почему бы не использовать std::vector<PlayingCard>?

+1

Это не поможет, вы просто будете разыгрывать «NULL» во второй раз через деструктор. –

+0

@ Сет, большое спасибо за замечание. –

+0

Вам также не нужно устанавливать '_playing_cards [i]' в 'NULL'. –

2

Ваш Deck деструктор должен читаться следующим образом:

Deck::~Deck() 
{ 
    for(int i = 0; i < _total_playing_cards; ++i) 
    { 
     delete _playing_cards[i]; 
    } 
    delete[] _playing_cards; 
} 

Обратите внимание, что в цикле, вы должны использовать не-массив удалить, чтобы удалить одну игральную карту.

Существует также еще одна гораздо большая проблема, а именно, что вы дважды вызываете деструктор - один раз в вашем явном вызове, а во второй раз, когда колода выходит из области видимости в конце main(). В принципе, вы никогда не должны называть деструктор на объект, не выделенный кучей вручную, на C++, поскольку вы мешаете встроенному управлению жизненным циклом объектов C++. Плохая идея, если вы (а) действительно не знаете, что делаете, и (б) вы делаете это в особых обстоятельствах.

Как в стороне, использование динамически выделенного массива указателей со всеми накладными расходами, которые это приносит, является плохой идеей, если вы не изучаете указатели и не экспериментируете с ними. В производственном коде, пожалуйста, сделайте сами и всех остальных пользу и вместо этого используйте std :: vector.

+0

Согласился на использование векторов, но это академический проект, поэтому мои руки грустно связаны с тем, что я могу и не могу использовать. –

+0

Отсюда комментарий «если вы не узнаете о указателях» :). На мой взгляд, это хорошая идея, чтобы узнать о них, но в современном коде на C++ существует много законных применений, чем люди, которым вы верите. –

0

Вам не нужно называть колоду. ~ Deck(); самостоятельно. Он будет вызываться автоматически. Просто используйте:

int main() 
{ 
    Deck deck(52); 
    return 0; 
} 

И использовать delete _playing_cards[i]; в цикл, так как delete[] средство для удаления массива и delete означает удалить только один элемент.

Смежные вопросы