Как оборона необходимо быть? [дубликат]

6
задан Community 23 May 2017 в 10:33
поделиться

14 ответов

В Code Complete 2, в главе об обработке ошибок, я познакомился с идеей баррикад. По сути, баррикада - это код, который строго проверяет все входящие в него данные. Код внутри баррикады может предполагать, что любой недопустимый ввод уже обработан, и что полученные вводные данные исправны. Внутри баррикады код должен беспокоиться только о недопустимых данных, переданных ему другим кодом внутри баррикады. Утверждение условий и разумное модульное тестирование могут повысить вашу уверенность в забаррикадированном коде. Таким образом, вы программируете очень оборонительно на баррикаде, но в меньшей степени внутри баррикады. Другой способ подумать об этом состоит в том, что на баррикаде вы всегда правильно обрабатываете ошибки, а внутри баррикады вы просто утверждаете условия в своей отладочной сборке.

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

Итак, почему в этом случае вы используете необработанный указатель? Что лучше: ссылку или умный указатель? Содержит ли указатель числовые данные, и если да, то было бы лучше заключить его в объект, который управлял жизненным циклом этого указателя?

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

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

Итак, почему в этом случае вы используете необработанный указатель? Что лучше: ссылку или умный указатель? Содержит ли указатель числовые данные, и если да, то было бы лучше заключить его в объект, который управлял жизненным циклом этого указателя?

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

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

Итак, почему в этом случае вы используете необработанный указатель? Что лучше: ссылку или умный указатель? Содержит ли указатель числовые данные, и если да, то было бы лучше заключить его в объект, который управлял жизненным циклом этого указателя?

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

почему в этом случае вы используете необработанный указатель? Что лучше: ссылку или умный указатель? Содержит ли указатель числовые данные, и если да, то было бы лучше заключить его в объект, который управлял жизненным циклом этого указателя?

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

почему в этом случае вы используете необработанный указатель? Что лучше: ссылку или умный указатель? Содержит ли указатель числовые данные, и если да, то было бы лучше заключить его в объект, который управлял жизненным циклом этого указателя?

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

16
ответ дан 8 December 2019 в 02:09
поделиться

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

Если переданный объект не должно быть нулевым, используйте ссылку! Или передайте по значению! Или используйте какой-нибудь умный указатель.

Лучший способ выполнять защитное программирование - это обнаруживать ошибки во время компиляции. If it is considered an error for an object to be null or point to garbage, then you should make those things compile errors.

Ultimately, you have no way of knowing if a pointer points to a valid object. So rather than checking for one specific corner case (which is far less common than the really dangerous ones, pointers pointing to invalid objects), make the error impossible by using a data type that guarantees validity.

I can't think of another mainstream language that allows you to catch as many errors at compile-time as C++ does. use that capability.

13
ответ дан 8 December 2019 в 02:09
поделиться

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

4
ответ дан 8 December 2019 в 02:09
поделиться

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

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

3
ответ дан 8 December 2019 в 02:09
поделиться

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

1
ответ дан 8 December 2019 в 02:09
поделиться

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

вы можете пропустить проверки нулевого указателя, если код находится в частном интерфейсе, над которым вы полностью контролируете, и / или вы проверяете наличие null, запустив модульный тест или какой-либо тест сборки отладки (например, assert)

0
ответ дан 8 December 2019 в 02:09
поделиться

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

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

В общем, я стараюсь не использовать «сырые» типы напрямую. Проиллюстрируем:

void myFunction(std::string const& foo, std::string const& bar);

Каковы возможные значения foo и bar ? Ну, это в значительной степени ограничено только тем, что может содержать std :: string ... что довольно расплывчато.

С другой стороны:

void myFunction(Foo const& foo, Bar const& bar);

намного лучше!

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

Я склонен отдавать предпочтение Strong Печатать. Если у меня есть запись, которая должна состоять только из алфавитных символов и содержать до 12 символов, я бы предпочел создать небольшой класс, обертывающий std :: string , с простым validate ], который используется внутри для проверки назначений и вместо этого передает этот класс. Таким образом, я знаю, что если я тестирую процедуру проверки ОДИН РАЗ, мне не нужно беспокоиться обо всех путях, по которым это значение может добраться до меня> оно будет проверено, когда оно достигнет меня.

Конечно, это мне не кажется, что код не следует тестировать. Это' Просто я предпочитаю сильную инкапсуляцию, а проверка ввода, на мой взгляд, является частью инкапсуляции знаний.

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

3
ответ дан 8 December 2019 в 02:09
поделиться

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

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

Один из методов защитного программирования - утверждать почти все, что вы можете. В начале проекта это раздражает, но позже это хорошее дополнение к модульному тестированию.

0
ответ дан 8 December 2019 в 02:09
поделиться

В этом вопросе есть несколько вещей, которые я хотел бы затронуть:

  1. В рекомендациях по кодированию должно быть указано, что вы имеете дело либо со ссылкой, либо со значением напрямую, вместо использования указателей. . По определению, указатели - это типы значений, которые просто хранят адрес в памяти - действительность указателя зависит от платформы и означает множество вещей (диапазон адресуемой памяти, платформа и т. Д.).
  2. Если вам когда-либо понадобится указатель для по любой причине (например, для динамически генерируемых и полиморфных объектов) рассмотрите возможность использования интеллектуальных указателей. Интеллектуальные указатели дают вам много преимуществ с семантикой «нормальных» указателей.
  3. Если тип, например, имеет «недопустимое» состояние, то сам тип должен обеспечивать это. В частности, вы можете реализовать шаблон NullObject, который указывает, как ведет себя «плохо определенный» или «неинициализированный» объект (может быть, генерируя исключения или предоставляя функции-члены без операций).

Вы можете создать интеллектуальный указатель, который выполняет NullObject по умолчанию выглядит следующим образом:

template <class Type, class NullTypeDefault>
struct possibly_null_ptr {
  possibly_null_ptr() : p(new NullTypeDefault) {}
  possibly_null_ptr(Type* p_) : p(p_) {}
  Type * operator->() { return p.get(); }
  ~possibly_null_ptr() {}
  private:
    shared_ptr<Type> p;
    friend template<class T, class N> Type & operator*(possibly_null_ptr<T,N>&);
};

template <class Type, class NullTypeDefault>
Type & operator*(possibly_null_ptr<Type,NullTypeDefault> & p) {
  return *p.p;
}

Затем используйте шаблон possible_null_ptr <> в тех случаях, когда вы поддерживаете, возможно, нулевые указатели на типы, которые имеют производное по умолчанию «нулевое поведение». Это делает явным в проекте, что существует приемлемое поведение для «нулевых объектов», и это делает вашу защитную практику документированной в коде - и более конкретной - чем общее руководство или практика.

Вы можете создать интеллектуальный указатель, который будет использовать NullObject по умолчанию, который выглядит следующим образом:

template <class Type, class NullTypeDefault>
struct possibly_null_ptr {
  possibly_null_ptr() : p(new NullTypeDefault) {}
  possibly_null_ptr(Type* p_) : p(p_) {}
  Type * operator->() { return p.get(); }
  ~possibly_null_ptr() {}
  private:
    shared_ptr<Type> p;
    friend template<class T, class N> Type & operator*(possibly_null_ptr<T,N>&);
};

template <class Type, class NullTypeDefault>
Type & operator*(possibly_null_ptr<Type,NullTypeDefault> & p) {
  return *p.p;
}

Затем используйте шаблон possible_null_ptr <> в тех случаях, когда вы поддерживаете, возможно, нулевые указатели на типы, которые имеют производные по умолчанию «нулевое поведение». Это делает явным в проекте, что существует приемлемое поведение для «нулевых объектов», и это делает вашу защитную практику задокументированной в коде - и более конкретной - чем общее руководство или практика.

Вы можете создать интеллектуальный указатель, который будет использовать NullObject по умолчанию, который выглядит следующим образом:

template <class Type, class NullTypeDefault>
struct possibly_null_ptr {
  possibly_null_ptr() : p(new NullTypeDefault) {}
  possibly_null_ptr(Type* p_) : p(p_) {}
  Type * operator->() { return p.get(); }
  ~possibly_null_ptr() {}
  private:
    shared_ptr<Type> p;
    friend template<class T, class N> Type & operator*(possibly_null_ptr<T,N>&);
};

template <class Type, class NullTypeDefault>
Type & operator*(possibly_null_ptr<Type,NullTypeDefault> & p) {
  return *p.p;
}

Затем используйте шаблон possible_null_ptr <> в тех случаях, когда вы поддерживаете, возможно, нулевые указатели на типы, которые имеют производные по умолчанию «нулевое поведение». Это делает явным в проекте, что существует приемлемое поведение для «нулевых объектов», и это делает вашу защитную практику документированной в коде - и более конкретной - чем общее руководство или практика.

0
ответ дан 8 December 2019 в 02:09
поделиться

Ряд ответов касается вопроса о том, как писать защиты в вашем коде, но мало что было сказано о том, «насколько вы должны защищаться?». Это то, что вам нужно оценить, исходя из важности ваших программных компонентов.

Мы занимаемся полетным программным обеспечением, и влияние ошибки программного обеспечения варьируется от незначительного раздражения до потери самолета / экипажа. Мы классифицируем различные части программного обеспечения на основе их потенциальных неблагоприятных воздействий, которые влияют на стандарты кодирования, тестирование и т. Д. Вам необходимо оценить, как ваше программное обеспечение будет использоваться, и влияние ошибок, и установить, какой уровень защиты вы хотите (и можете себе позволить). В стандарте DO-178B это называется «Уровень гарантии проектирования».

0
ответ дан 8 December 2019 в 02:09
поделиться

Если серьезно, это зависит от того, сколько ошибок вы хотели бы нанести вам.

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

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

РЕДАКТИРОВАТЬ для ясность: некоторые другие ответы говорят о модульных тестах. Я твердо уверен, что тестовый код иногда более ценнее, чем код, который он тестирует (в зависимости от того, кто измеряет значение). Тем не менее, я также считаю, что модульные тесты также необходимы, но недостаточны для защитного кодирования.

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

Итак, теперь вы называете свою защиту и хорошо протестированное методологическое мышление (которое, к сожалению, не имеет внутренней проверки нулевого указателя) и бум! NullPointerException, что без внутренней проверки у вас нет возможности справиться:

defensiveMethod(thirdPartySearch("Nothing matches me")); 
// You just passed a null to your own code.
3
ответ дан 8 December 2019 в 02:09
поделиться

«Модульные тесты, проверяющие, что код выполняет то, что он должен делать»> «производственный код пытается проверить, не выполняет ли он то, что он не должен делать».

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

2
ответ дан 8 December 2019 в 02:09
поделиться

Это очень сильно зависит; вызывается ли рассматриваемый метод кодом, внешним по отношению к вашей группе, или это внутренний метод?

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

Для методов, видимых извне - если они у вас есть - вы всегда должны дважды проверять вводимые данные. Всегда.

1
ответ дан 8 December 2019 в 02:09
поделиться

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

0
ответ дан 8 December 2019 в 02:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: