проблема с деструктором
-
22-08-2019 - |
Вопрос
это моя функция addCard, которая принимает PlayingCard в качестве параметра, а затем передает адрес самого себя выделенному массиву указателей на объекты PlayingCard.
void cardHand::addCard(playingCard card) {
theHand[nElems++] = &card;
} // addCard()
теперь, когда я запускаю свою программу, она работает нормально, но затем выходит из строя при вызове деструктора.
cardHand::~cardHand() {
for(int c = 0;c<MAX;c++) {
if(theHand[c] != NULL)
delete theHand[c]; // here is the problem
}
delete [] theHand;
} // class destructor
происходит ли сбой из-за того, что я передаю адрес объекта PlayingCard только в функции addCard.должен ли это быть указатель вместо этого?
Решение
Проблема заключается вот в чем
void cardHand::addCard(playingCard card) { theHand[nElems++] = &card; }
Вы сохраняете адрес временного объекта card, который будет уничтожен в конце метода addCard.
И в вашем деструкторе вы пытаетесь удалить его снова.
У вас есть два варианта.
Первый:создайте addCard, чтобы принимать только конфигурацию карты, и создайте свою карту с new
в addCard
способ.
Второй:принимайте карту по указателю, но тогда деструктор вашего cardHand не может нести ответственность за удаление карты.А удаление выполнит объект колоды, который создал все карты.
Другие советы
Когда ты говоришь:
theHand[nElems++] = &card;
Вы сохраняете адрес параметра функции, который фактически является локальной переменной.Это всегда плохо, и в вашем случае это приводит к сбою при попытке удалить его.
Вы, вероятно, хотите что-то вроде:
theHand[nElems++] = new playingcCard( card );
но реальное решение состоит в том, чтобы использовать std::vector для PlayingCard и полностью отказаться от динамического распределения.
Вы используете C ++ как лучший C, и хотя это прекрасно для многих целей, это не идиоматический C ++.
Чего вы действительно хотите, так это полностью отказаться от динамического распределения.В общем, если вы пишете C ++, вам следует очень редко использовать new
и еще реже используют delete
.
Ваша рука должна быть объявлена примерно так:
std::vector< playingCard > hand;
Новые карты должны быть нанесены на него чем-то вроде этого:
hand.push_back( card );
Вы должны обнаружить, что, используя классы коллекции библиотеки C ++ (и интеллектуальные точки TR1), вам никогда не нужно использовать new
или delete
-- в любом случае, до тех пор, пока ты не начнешь писать свои собственные умные указатели.
Итак, учитывая другие ответы, мой способ сделать это - передать ссылку или указатель на playingCard
.
И что касается delete
, общее правило таково, что вы только delete
что ты new
ed, т. е.тот, кто выделяет память, должен нести ответственность за ее утилизацию.Конечно, как и в случае с любым правилом, из этого есть исключения, но такое поведение должно быть очень хорошо задокументировано в контракте интерфейса.
Он разбивается на delete
потому что он никогда не был выделен с new
.
void cardHand::addCard(playingCard card) {
theHand[nElems++] = &card;
} // addCard()
При вызове этой функции вы передаете временную копию игровой карты и записываете ее адрес.Хранение временных адресов запрещено, если вы действительно не знаете, что делаете.
Вместо этого измените это на что-то вроде
/** @param card card to add. Takes ownership. */
void cardHand::addCard(playingCard *card) {
theHand[nElems++] = card;
} // addCard()
И в вызывающем коде передайте ему playingCard
указатель на объект, возвращенный из new playingCard
.
У него все еще есть проблема с передачей права собственности на объект, которая является распространенной причиной ошибок двойного удаления или утечек памяти.Хорошей привычкой является делать такие переводы явными с помощью комментариев к коду.
Правило глухого удара:Удаляйте только то, что вы выделили (либо с помощью new, либо с помощью memalloc)
Следовательно, ваш крах.
Причина, по которой это не работает, заключается в том, что вы передаете свой класс по значению в cardHand::addCard
.
Итак, что делает компилятор, так это создает временную копию экземпляра вашего класса в стеке.
Поскольку он находится в стеке, он будет автоматически очищен, как только addCard
возвращает функция.
Вы можете исправить это, передав свой playingCard
экземпляр вместо этого в качестве указателя.
Однако, как уже говорили другие в этом посте, желательно не delete
вещи, которые вы явно не new
.