Хорошая практика программирования всегда проверять нулевые указатели перед использованием объекта на С++?

Это похоже на большую работу; для проверки значения null каждый раз, когда объект используется.

Мне сообщили, что неплохо проверить нулевые указатели, поэтому вам не нужно тратить время на поиск ошибок сегментации.

Просто интересно, что думает сообщество здесь?

Ответ 1

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

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

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

Ответ 2

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

int * p = new int;

то p никогда не может быть NULL, потому что new будет генерировать исключение, если сбой распределения.

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

// does something with p
// note that p cannot be NULL
void f( int * p );

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

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

Ответ 3

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

В моих проектах на С++ я проверяю, является ли указатель (если я вообще использую указатели) NULL, только если это может быть допустимое состояние указателя. Проверка на NULL, если указатель никогда не должен быть NULL, немного бессмысленна, потому что вы пытаетесь решить какую-либо ошибку программирования, которую вы должны исправить.

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

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

Ответ 4

Я ненавижу количество проверок кода для добавления нулей, поэтому я делаю это только для функций, которые я экспортирую другому человеку.

Если я использую внутреннюю функцию, и я знаю, как я ее использую, я не проверяю нули, так как это приведет к слишком запутанному коду.

Ответ 5

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

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

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

Ответ 6

Я полагаю, что могу сделать много проверок для указателей NULL для стоимости (отладки) только одного segfault.

И удар производительности пренебрежимо мал. ДВА ИНСТРУКЦИИ. Test for register == zero, branch, если тест завершен. В зависимости от машины, может быть, только одна инструкция, если нагрузка на регистр устанавливает коды условий (и некоторые из них).

Ответ 7

Другие (AshleysBrain и Neil Butterworth), уже ответили правильно, но я подведу его здесь:

  • Использовать ссылки как можно больше
  • При использовании указателей инициализируйте их либо NULL, либо действительный адрес/объект памяти
  • Если вы используете указатели, всегда проверяйте, являются ли они NULL перед их использованием.
  • Использовать ссылки (снова)... Это С++, а не C.

Тем не менее, есть один угловой случай, когда ссылка может быть недействительной /NULL:

void foo(T & t)
{
   t.blah() ;
}

void bar()
{
   T * t = NULL ;
   foo(*t) ;
}

Компилятор, вероятно, скомпилирует это, а затем при выполнении код будет разбиваться на строку t.blah() (если T:: blah() использует this так или иначе).

Тем не менее, это обман/саботаж: писатель функции bar(), разыменованный t без проверки t, не был нулевым. Таким образом, даже если авария происходит в foo(), ошибка находится в коде bar(), и ответственный за bar() отвечает.

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

И если вам действительно нужно использовать указатель на С++, если вы на 100% уверены, что указатель не является NULL (некоторые функции гарантируют что-то подобное), тогда всегда проверяйте указатель.

Ответ 8

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

Ответ 9

Если указатели приходят к вам в качестве параметров функции, убедитесь, что они действительны в начале функции. В противном случае нет особого смысла. new генерирует исключение при ошибке.