Удаление памяти, когда она была удалена ранее - PullRequest
2 голосов
/ 17 февраля 2012

У меня есть небольшая программа на C ++, где я создаю два объекта класса Person. Этот класс имеет char *m_szFirstName и char *m_szLastName для данных.

Затем я назначаю один объект другому, в результате чего оба элемента данных объекта указывают на одно и то же местоположение.
В деструкторе я удаляю память, выделенную для первого объекта, и присваиваю указателям значения NULL. Как то так.

if (m_szFirstName!= NULL)
{
    delete [] m_szFirstName;
    m_szFirstName = NULL;
}

Затем, когда я иду, чтобы удалить память для второго объекта, проверка для NULL не работает, и когда я удаляю память, я получаю сбой. Из отладчика это показывает, что мой указатель не NULL. Имеет 0xfeee.

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

Ответы [ 6 ]

4 голосов
/ 17 февраля 2012

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

Если вам нужно явно объявить деструктор, конструктор копирования или оператор копирования, вы, вероятно, должны явно объявить все три из них.

В вашем случае Вы не определяете оператор присваивания копии, что приводит к полному копированию указателя.

Предлагаемое решение:

Если вы можете использовать std::string вместо char *, просто просто используйте std::string, он имеет первое и главное преимущество перед любыми глупыми указателями.
Вы можете обойтись без всяких прикольных указателей, используя std::string.

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

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

Для этого следует использовать Умный указатель , который будет неявно управлять динамической памятью указателя. Использование умного указателя обеспечит неявное освобождение динамической памяти после использование и вам не нужно вручную управлять им.

Сценарий, который у вас есть, является той самой причиной, по которой в C ++ вы должны полагаться на RAII , а не на ручное управление ресурсами, и использование Smart-указателя - это путь, который вам нужен .

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

Какой указатель мне использовать, когда?

для выбора интеллектуального указателя.

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

С

if (m_szFirstName!= NULL)
{ 
  delete [] m_szFirstName;
  m_szFirstName = NULL;
}

Вы устанавливаете только для m_szFirstName значение NULL, а не m_szLastName. Это означает, что у вас должен быть какой-то способ отследить тот факт, что они указывают на одно и то же место. Есть ли причина, по которой они указывают на одно и то же место? Не могли бы вы скопировать имя вместо того, чтобы указывать указатели на одно и то же место?

Если вам нужны два указателя для совместного использования одних и тех же данных, я бы посмотрел на std :: tr1 :: shared_ptr, который решит эту проблему для вас, отслеживая количество ссылок и удаляя при этом число ссылки достигают 0.

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

Просто прекратите устанавливать указатели в NULL, когда вы удалили объект.Как видите, это только приводит к боли.Вы не можете предполагать, что, поскольку указатель не равен NULL, он еще не удален.

Вы можете использовать любой разумный шаблон, который хотите избежать, чтобы избежать этой проблемы.Например, Boost's shared_ptr - отличный выбор.

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

Ваша проблема - классическая проблема C / C ++, которая известна как проблема "Dangling Pointer".Разыменование висящего указателя привело к падению.Проблема в подсчете ссылок.После того, как вы назначите ту же память второму указателю, тогда счетчик ссылок должен быть равен 2. Таким образом, если вы удалите один счетчик ссылок, указатель должен стать 1, и ваша память не должна быть освобождена или освобождена, если счетчик не равен 0. При 0 он может быть собран.

Теперь есть хорошие ответы, чтобы решить вашу проблему.Поскольку вы используете C ++, вы можете использовать что-то вроде auto_ptr (ИЛИ shared_ptr).Они предоставляют то, что я упомянул выше, подсчет ссылок, и даже если вам не нужно беспокоиться об удалении или освобождении ваших объектов, эти классы позаботятся.Они работают над тем, что называется шаблоном RAII, где деструктор автоматически вызывается, когда объект в стеке выходит из области видимости.

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

Если у вас есть два указателя, указывающие на одно и то же место (после того, как вы присвоили первый указатель второму), у вас есть два указателя, указывающие на один и тот же адрес.Удаление одного освобождает память, на которую указывают оба.Но установка одного на NULL не изменяет другой указатель.То же самое происходит, если у вас есть два целых числа, например.

int a = 3;
int b = a;

Теперь, если вы запустите

a = 0;

, значение b не изменится.Точно так же ваш второй указатель не изменяется, когда вы изменяете первый (но когда вы изменяете память, указанную одним указателем, вы можете видеть эффект и через другой).

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

Не удаляйте его снова, если (m_szFirstName == m_szLastName).Но это приведет к утечке памяти (при назначении одного указателя другому).

...