Почему приведенный ниже фрагмент кода не дает сбой, хотя я удалил объект?

<code>class object
{
  public:
    void check()
    {
      std::cout<<"I am doing ok..."<<std::endl;
    }
};

int main()
{
  object *p = new object;
  p->check();
  delete p;
  p->check();
  delete p;
  p->check();
}
</code>

EDIT: Gurus, i am confused by many of the statements "it may crash or may not".. why isnt there a standard to say, this how we deal with a block of memory that is deleted using 'delete operator'..? Any inputs ?

 Warrior17 июн. 2009 г., 13:20
Гуру, меня смущают многие из утверждений «это может произойти сбой или может не произойти». Почему нет стандарта, чтобы сказать, как мы поступаем с блоком памяти, который удаляется с помощью «оператора удаления»…? Любые входы?
 ChrisF♦17 июн. 2009 г., 13:30
Это "может или не может" падение, потому что поведение не определено и зависит от компилятора, операционной системы и того, что еще происходит. По сути, удаление просто помечает указатель как снова доступный. Ничего явно не делается с памятью, на которую указывает, так что вы можете обойтись без нее, если ничего не изменилось.

Ответы на вопрос(10)

Удалить только освобождает память и делает ее доступной обратно в кучу.

Значение указателя не определено после вызова delete, поэтому может произойти сбой, а может и нет.

Совет по программированию, который я использую для уменьшения ошибок программирования, заключается в том, чтобы после удаления установить указатель на NULL. Таким образом, вы знаете, что не случайно используете указатель после того, как он был удален.

 17 июн. 2009 г., 14:44
После удаления указатель ссылается на освобожденное хранилище и становится недействительным. Что это означает, точно еще не определено:open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#312 .
 17 июн. 2009 г., 12:38
Вы все еще можете использовать указатель. Должна быть сделана проверка на NULL, но я думаю, это то, что вы имели в виду.
 17 июн. 2009 г., 13:14
Значение указателя не является неопределенным, оно точно такое же, как и до удаления.
 17 июн. 2009 г., 12:43
Подлинная репутация, но совершенно неприменимая к этой «проблеме».
 17 июн. 2009 г., 12:49
+1 за упоминание установки указателя на NULL. Также отлично подходит для отладки, поскольку удаленные указатели не выглядят так, как будто они все еще могут быть действительными.

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

class object
{
  public:
    void check()
    {
      std::cout<<"I am doing ok..."<<std::endl;
    }
};

int main()
{
  object *p = 0;
  p->check();
}

Я думаю, что это зависит от среды (платформа / компилятор) ...
Это, по крайней мере, дает неожиданное поведение. Я думаю, вам повезло, что в вашем случае это не сработало ;-)

Чтобы добавить немного к тому, что сказали другие, попробуйте использовать переменную-член вашего класса внутри метода check (), а затем посмотрите, что произойдет.

Вызов функции в случае классов аналогичен обычному вызову функции, за исключением одного небольшого различия. Аргументы помещаются в стек в обоих случаях, но в случае вызова функции объекта, 'this' ' помещается в стек в качестве первого аргумента. Это "это" используется внутри функции для доступа к переменным-членам. Поскольку вы не обращаетесь ни к каким переменным-членам, это работает. И да, он может работать, даже если вы обращаетесь к переменным-членам, но вы получите неожиданное поведение, потому что компилятор может свободно использовать память, выделенную для & p; Как только компилятор использует эту память, у вас начнутся сбои. Например, если вы объявите дополнительные переменные после удаления перед повторным использованием «p», это может привести к сбою.

Изменить: Кроме того, если ваш метод не имеет доступа к какой-либо переменной-члена, то это определенный кандидат на статический метод.

 17 июн. 2009 г., 12:48
Как я объяснил ранее, удаление "p" просто означает, что память свободна для использования компилятором. Внутренне, место, на которое указывает указатель "p"; указывает все еще держит действительный адрес. Попробуйте объявить еще несколько переменных-указателей, прежде чем использовать & p; p & apos; и это может привести к сбою. Это даст вам неожиданное поведение. Никто не может сказать вам, что, когда именно он рухнет.
 Warrior17 июн. 2009 г., 12:40
Братан, я изменил код следующим образом, но он не вылетает: class object {int p; public: object () {p = 100; } void check () {std :: cout & lt; & lt; & quot; p = & lt; & lt; p & lt; & lt; std :: endl; std :: cout & lt; & quot; я делаю хорошо ... & lt; & lt; std :: endl; }}; & GT; & GT; ./test p = 100 У меня все хорошо ... p = 100 У меня все хорошо ... p = 100 У меня все хорошо ...

1) Зависит от компилятора, в Mac OS с gcc 4.0.1:

g++ -Wall -g -c main.cpp -o main.o
g++ -o x main.o 
./x
I am doing ok ...
I am doing ok ...
x(5857) malloc: *** error for object 0x100150: double free
*** set a breakpoint in malloc_error_break to debug
I am doing ok ...

Двойное освобождение вызывает проблемы.

2) Как правило, удаляемый указатель, который уже был удален, не определен, вы всегда должны устанавливать указатель на 0 после удаления, вызов удаления для указателя со значением 0 разрешен.

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

Даже если бы вызов метода использовал указатель this, это не гарантировало бы сбой. При удалении указатель p не обнуляется, поэтому он все еще указывает на тот же адрес в памяти. В зависимости от реализации new / delete и менеджера кучи эта память может даже не использоваться повторно, поэтому использование p может продолжать работать, даже если память была освобождена.

Это даже смешнее, чем это. Это компилирует & amp; работает красиво:

#include <iostream>
using namespace std;
class object {
    public:
      void check() {
          cout << "I am doing ok..." << endl;
      }
};

int main() {
   object *p = (object*)0;
   p->check();
   return 0;
}

На оболочке:

$ g++ -o t t.cc
$ ./t
I am doing ok...
$

:) На самом деле вам не нужен объект для вызова этого метода! ура, ч

Поскольку функция ничего не делает с данными элемента объекта илиthis указатель.

Это похоже на вызов функции

void check(object *self)
{
  std::cout<<"I am doing ok..."<<std::endl;
}

с неверным указателем в качествеself аргумент.

Существует двойное удаление, которое может привести к сбою в некоторых средах.

 17 июн. 2009 г., 12:39
Это даже смешнее, чем это. Это компилирует & amp; работает хорошо: #include & lt; iostream & gt; использование пространства имен std; объект класса {public: void check () {cout & lt; & lt; "У меня все хорошо ..." & Л; & л; епсИ; }}; int main () {объектp = (object) 0; р & GT; проверить (); вернуть 0; } $ g ++ -o t t.cc $ $ ./t У меня все хорошо ... $ :) На самом деле вам не нужен объект для вызова этого метода! ура, ч.
 17 июн. 2009 г., 12:47
@Aamir: Невиртуальные функции не хранятся ни в одной таблице, они разрешаются во время ссылки. Виртуальные функции разрешаются во время выполнения с использованием vtable объекта.
 17 июн. 2009 г., 12:40
Ой, дорогой, лежал запутался. Буду репостить как самостоятельный ответ ...
 17 июн. 2009 г., 12:43
извините тип в ссылке выше, все функции хранятся в таблице функций, а не объекты.
 17 июн. 2009 г., 12:42
Да, потому что методы не являются частью объекта. Методы являются частью класса. Попробуйте вспомнить принципы ОО. Атрибуты и поведение. Атрибуты связаны с объектами, а поведение - на уровне класса. Все объекты ведут себя одинаково. За кулисами все объекты хранятся в виде таблицы функций, и вызов функции - это просто переход указателя инструкции к начальному адресу функции. Для более подробной информации, посмотрите мой ответ на этот вопрос.

при удалении не устанавливается значение p, равное нулю, поэтому оно все равно указывает на область памяти. Это зависит от реализации new / delete, но обычно удаляет только ту часть памяти, которая доступна для любых новых выделений.

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

Решение Вопроса

Потому что то, на что это действительно похоже после компиляции, выглядит примерно так:

object::check( object* this )
{
     // do stuff without using this
}

int main()
{        
     object *p = new object;
     object::check( p );
     delete p;
     object::check( p );
     delete p;
     object::check( p );
 }

Поскольку вы «не касаетесь» этого, вы фактически не имеете доступа к какой-либо плохой памяти.

Хотя удаление p дважды должно привести к сбою:

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.2

 17 июн. 2009 г., 21:26
Относительно удаления 2x, возможно, вызывающего сбой ... да, именно поэтому я обычно устанавливаю & quot; p & quot; 0 после удаления ... а) так что я знаю, что он больше не указывает на объект, и б) потому что безопасно вызывать удаление по нулевому указателю.
 18 июн. 2009 г., 06:55
Во многих кодовых базах, которые я видел, было что-то вроде "#define SAFE_DELETE (x) delete x, x = NULL" quot; макрос, или еще лучше шаблон, по этой причине. Это одно из немногих мест, где мне нравится оператор запятой - назначение delete и NULL должно «принадлежать» к тому же утверждению в большинстве случаев, насколько я понимаю.

Ваш ответ на вопрос