Освобождение памяти для массива указателей? - PullRequest
0 голосов
/ 04 февраля 2012

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

PlayingCard** _playing_cards;

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

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);
    }
}

Освобождение памяти, выделенной с помощью 'new', обрабатывается деструктором:

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

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

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

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

Ответы [ 4 ]

2 голосов
/ 04 февраля 2012

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

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

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

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

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

1 голос
/ 04 февраля 2012

Пожалуйста, не вызывайте деструктор напрямую в 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>?

0 голосов
/ 04 февраля 2012

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

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

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

0 голосов
/ 04 февраля 2012

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

  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 в другом? Это излишне усложняет вещи, я бы посоветовал выбрать один (желательно последний) и придерживаться его.

...