2015-03-18 2 views
14

C++ В нашей базе кода у нас есть много конструкций, как это:Safe разыменования указателя в

auto* pObj = getObjectThatMayVeryRarelyBeNull(); 
if (!pObj) throw std::runtime_error("Ooops!"); 
// Use pObj->(...) 

В 99,99% случаев эта проверка не срабатывает. Я думаю о следующем решении:

auto& obj = deref_or_throw(getObjectThatMayVeryRarelyBeNull()); 
// Use obj.(...) 

Где deref_or_throw объявлен следующим образом:

template<class T> T& deref_or_throw(T* p) { 
    if (p == nullptr) { throw std::invalid_argument("Argument is null!"); } 
    return *p; 
} 

Этот код гораздо понятнее и работает как мне нужно.

Вопрос: я изобретаю колесо? Есть ли какое-то смежное решение в стандартном или импульсном режиме? Или у вас есть некоторые комментарии к решению?

PS. Связанный с этим вопрос (без ответа): Is there a C++ equivalent of a NullPointerException

+0

Есть ли вообще код, который действительно имеет дело с делом «nullptr»? Например. что-нибудь вроде 'if (! obj) {std :: cout <<« Нуль, но все равно, переходите к чему-то значимому \ n "; } else {std :: cout << "Non-Null, что-то другое \ n"; } '. Или это случай «nullptr» всегда «выход», потому что это неправильно? -path? – stefan

+0

Этот код существует, но встречается редко. В большинстве случаев мы просто не можем продолжать. Мы ничего не можем сделать. Вся рутина не имеет смысла. Так что, возможно, существование этого объекта является _precondition_? И мне нужно проверить это, а затем вручную разыменовать указатель, не беспокоясь? – Mikhail

+1

Именно это вам нужно уточнить для себя, имхо. Если это предварительное условие, возможно, 'assert'ion лучше подходит. В противном случае это явно исключительная вещь (0,01% является довольно исключительной), поэтому «исключение» является подходящим. Афаик, в библиотеке нет такой вещи. – stefan

ответ

4

Существует два способа решения проблемы «редкого случая с нулевым указателем». Во-первых, это предлагаемое исключение. Для этого метод deref_or_throw - хорошая вещь, хотя я бы предпочел выбросить runtime_error, а не исключение invalid_argument. Вы также можете подумать о том, чтобы назвать его NullPointerException, если хотите.

Однако, если случай ptr != nullptr на самом деле является предварительным условием для алгоритма, вы должны стараться изо всех сил добиться 100% -ной безопасности при наличии этого не нулевого случая. В этом случае подходящей является assert.

assert Преимущества:

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

Недостатки assert:

  • Потенциальный неопределенное поведение в режиме выпуска

Вы должны также рассмотреть вопрос о написании метода getObjectThatMayNeverBeNullButDoingTheSameAsGetObjectThatMayVeryRarelyBeNull(): метод, который гарантированно возвращает ненулевой указатель объект, который в остальном полностью эквивалентен вашему первоначальному методу. Однако, если вы можете пройти лишнюю милю, я бы настоятельно рекомендовал не возвращать необработанный указатель. Embrace C++ 11 и вернуть объекты по значению :-)

+0

Я хотел бы иметь утверждения, которые не удаляются в релизе. И я хотел бы различать утверждения и предварительные условия. Любые хорошие ссылки на эту тему? – Mikhail

+0

Утверждения являются предпосылками (или, по крайней мере, ближе всего к ней). Но ничто не мешает вам использовать как утверждение, так и исключение. :) – stefan

+0

Да, решения настолько очевидны, что я уверен, что кто-то хорошо поработал над этим и имеет на нем последовательную и общую структуру. Хотя я не знаю. Например, у Boost.assert нет никаких предварительных условий. – Mikhail

1

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

В общем, полезно, если возможно, удалять null ссылки из кода. Другими словами, вы можете запрограммировать на инвариант, что «здесь здесь не будет нуля ... или там». Это имеет много преимуществ. Вам не придется обфускать код, обертывая методы в deref_or_throw. Вы можете получить больше семантического значения из кода, потому что, как часто вещи null в реальном мире? Ваш код может быть более читаемым, если вы используете исключения для указания ошибок, а не null. И, наконец, вы уменьшаете необходимость в проверке ошибок, а также уменьшаете риск ошибок времени выполнения (разыгрывание ужасных нулевых указателей).

Если ваша система не была разработана с null случаев в виду, я бы сказал, что лучше оставить его в покое и не сходят с ума оборачивать все с deref_or_throw. Возможно, возьмите подход Agile. Когда вы кодируете, проверьте контракты, которые предлагают объекты класса в качестве сервисов. Как часто можно ожидать, что эти контракты будут возвращены null? Что такое семантическое значение null в этих случаях?

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

+0

Функция 'getObjectThatMayVeryRarelyBeNull()' должна иметь возможность возвращать 'null'. Это редко, но это возможно. Но так как это редко, большинство процедур (кроме некоторых специальных) ожидают, что он вернет объект. В противном случае они просто бессмысленны. – Mikhail

+0

У меня возникло бы желание удалить null как действительное возвращаемое значение и, возможно, рассмотреть другой вариант, чтобы указать на этот очень редкий случай. Не зная вашего варианта использования, я не могу точно рассказать о том, что это будет. Похоже, ваш класс может вернуть «неожиданный» результат, который не все иждивенцы будут обрабатывать должным образом, поэтому кажется, что действительно действительно вернуть туда null. И, конечно, будущим читателям вашего кода не нужно было бы подбирать тонкость, что 'getObjectThatMayVeryRarelyBeNull()' возвращает нуль 0.01% времени –

+0

Это действительно сложный вопрос дизайна, и именно поэтому я не спрашиваю его :) Я ограничиваю область возможных решений, так что интерфейс 'getObjectThatMayVeryRarelyBeNull()' не может быть изменен. – Mikhail

 Смежные вопросы

  • Нет связанных вопросов^_^