Поведение простого кода с указателями в C ++ - PullRequest
1 голос
/ 21 февраля 2011

Предположим, у нас есть следующий код:

sphere * a, * b;
a = new sphere(1.0);
b = a;
b->setRadius(2.0);
delete b;
a->setRadius(4.0);
sphere * c = new sphere(5.0);
b = new sphere(3.0);
cout << a->getRadius() << endl;

Каков будет результат:

(a) 4.0
(b) 3.0
(c) A segmentation fault.
(d) Compiler error.
(e) The behavior cannot be predicted.

Я не думаю, что вам нужно будет видеть класс сферы, так как он довольно хорошочевидно, что происходит.Теперь, когда мы устанавливаем b=a, это означает, что b указывает на a.Затем мы delete b, что означает, что мы удаляем то, на что указывал b, и это означает, что a больше не указывает ни на что.Поэтому, когда мы пытаемся сказать a->setRadius, мы не можем.И поэтому я думаю, что это seg fault.Я смотрю на это правильно?

Ответы [ 4 ]

14 голосов
/ 21 февраля 2011

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

2 голосов
/ 21 февраля 2011

До delete b; все в порядке. Следующая строка (a->setRadius(4.0);) вызовет неопределенное поведение, поскольку вы обращаетесь к удаленному объекту. Проблема в том, что в 90% случаев он будет работать (да, это проблема, потому что в остальных 10% случаев он будет падать, и вы потеряете ночь, выясняя причину!)

Почему это? Это происходит потому, что многие реализации операторов new и delete не «запрашивают» память у ОС при каждом вызове. Они просят большой блок этого и затем используют это немного за один раз. Когда вы удаляете свой объект, память только «помечается» как свободная и не освобождается. Поэтому, когда вы обращаетесь к освобожденному объекту, вы обращаетесь к памяти, которая (вероятно) все еще выделена вашему процессу, но которую вы не должны использовать. В середине какая-то другая часть вашей программы могла бы выделить ее и изменить, или просто оператор удаления мог увидеть, что там было так много памяти, «помеченной как свободная», и решил вернуть ее в ОС, вызывая SEGFAULT, если вы попробуйте получить к нему доступ. «Отладочная» версия удаления может заполнить его шаблоном или обнулить его, так что если вы посмотрите его, станет более очевидным, что он используется или свободен ... Есть много вариантов.

Примечание: я упростила обработку памяти новыми и удаляемыми. «Обычно» есть еще несколько уровней косвенности. Но они не имеют отношения к обсуждению.

В некоторых реализациях new / delete / malloc / free при отладке строит память, вместо обнуления, заполняется 0xcc или 0xcdcd. Это по двум причинам. Если я правильно помню, во-первых, 0xcc - это инструкция Intel для точки останова INT 3 (а 0xcdcd - это кратное число или что-то подобное). Другая причина в том, что если вы смотрите его в отладчике, шаблон будет совершенно очевиден, в то время как шаблон «все 0x00» будет сложнее различить.

1 голос
/ 21 февраля 2011

Это неопределенное поведение, потому что вы удаляете сферу, на которую указывают a и b.После удаления они оба указывают на недопустимый объект сферы (это уже не сфера, а просто память).a->setRadius(4.0) может вызвать ошибку сегмента вместе с a->getRadius.

Чтобы ответить на ваш вопрос с несколькими вариантами ответа, e будет правильным.

1 голос
/ 21 февраля 2011

С delete b; вы освобождаете область памяти, на которую ранее указывали оба указателя a,b. Итак, a->setRadius(4.0); поведение не определено. Неопределенным означает, что он может работать правильно, как вы ожидали, или может привести к ошибке сегментации, или что-то может произойти.

Поскольку a является висящим указателем, a->getRadius(); также приведет к неопределенному поведению.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...