Подтвердить что ты не робот

Есть ли смысл временно делать указатель NULL?

Я видел много таких кодов:

SomeType* ptr = NULL;
ptr = SomeMethod(some, params);

Какой смысл? Я также видел, где ptr объявлен где-то в другом месте (например, в определении класса), а затем в конструкторе класса будет следующее:

ptr = NULL;
ptr = SomeMethod(some, params);

Я не понимаю, почему это делается. Разумеется, строка ptr = NULL бесполезна?

4b9b3361

Ответ 1

если "SomeMethod" выдает исключение, ваш SomeType * будет указывать на то, на что вы не хотите его указывать. Следовательно, определенно хорошая практика - установить указатель на нуль, если вы не хотите, чтобы он больше указывал на старое.

Ответ 2

Помимо очень хороших точек бросания исключений, которые применяются к С++, даже в C, неплохо было бы инициализировать все переменные. Компилятор почти всегда оптимизирует инициализацию, когда она может определить, что она не нужна.

Вот небольшой пример того, что может произойти, если вы этого не сделаете.

SomeType* ptr;
ptr = SomeMethod(some, params);

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

Теперь кто-то решает, что это хорошая идея добавить немного больше кода:

SomeType* ptr;

int x = someotherfunction;

if (x > 90)
{
     ptr = SomeMethod(some, params);
}

Теперь у вашего ptr будет какое-то случайное бессмысленное содержание - может быть NULL, но, скорее всего, что-то еще. Да, мы все думаем, что будем помнить, чтобы проверить, не повлияли ли наши изменения на что-то еще и т.д. И т.д. Но если вы программируете в течение нескольких лет, я почти наверняка у вас было несколько случаев "Ой, не думал об этом".

Предположим, что новый код, в который я добавил "someotherfunction", составляет 20 или 30 строк, и вы можете четко видеть проблему.

Ответ 3

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

Во втором случае это совершенно бесполезно.

Ответ 4

Это бесполезно (это называется "мертвым назначением" ). Однако некоторые предпочитают инициализировать указатели на NULL при объявлении, а затем назначая его явно, отдельно. Возможно, это просто по соображениям удобочитаемости (возможно, некоторые считают его более читабельным или считают полезным объявить переменную, даже если, например, задание закомментировано для отладки). Я не уверен, что сделаю это/рассмотрю это как практику.

Также обратите внимание, что большинство компиляторов автоматически оптимизируют присвоение.

Ответ 5

Не только эта практика бесполезна; это вредно. Если вы только что написали:

SomeType* ptr = NULL;
/* lots of stuff in between */
ptr = SomeMethod(some, params);

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

SomeType* ptr;
/* lots of stuff in between */
ptr = SomeMethod(some, params);

то любое использование ptr до назначения UB, и хороший инструмент компилятора или статического анализа проинформирует вас об этой ошибке.

Ответ 6

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

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

Иногда кажущиеся бесполезными инициализации переменных происходят в результате того, что кто-то должен был отключить предупреждение компилятора о "потенциально неиспользуемой" переменной, что может произойти, если есть несколько способов достичь блока кода, в котором используется переменная, и компилятор недостаточно изощрен, чтобы доказать, что во всех случаях переменная каким-то образом инициализируется присвоением.

Инициализация всех переменных в точке, где они объявлены, является хорошей привычкой, поскольку она уменьшает потенциал непредсказуемого поведения в результате неинициализированных данных. Некоторые утверждают, что некоторые статические инструменты работают менее эффективно, когда неинициализированные переменные стираются с инициализацией, что дает им недопустимые значения, что затрудняет поиск ошибок. Тем не менее, ошибки более повторяемы, если они основаны на инициализированных данных, а поведение с ошибками вполне вероятно также переносится и среди платформ. Некоторые языки инициализируют ваших локальных жителей. В Lisp (let (x)...) означает, что в кодовом блоке ... переменная x имеет значение NIL, так как нет инициализатора. Статический анализ кода Lisp может легко отслеживать факты, такие как NIL, ненадлежащим образом передается числовой функции. В C и родственных языках проблемы немного разные, потому что для типа типа int нет эквивалента значения неправильного типа, такого как NIL. Таким образом, 0 заменяется, что является допустимым значением и не будет просто взорвать вычисления с несоответствием типа. Недопустимый 0 может привести к алгоритмической ошибке при вычислении. Однако в случае указателей значение нулевого указателя является разумным факсимилем NIL в том, что оно является видимым в программе, переносимым значением домена, которое гласит: "Я не указываю ни на какой объект". Когда указатель фактически пока не указывает на что-либо, нам нужно дать ему это значение, а не оставлять в нем случайные биты, которые могут случайно указывать на непреднамеренный объект.