Насколько вам следует защищаться?[дубликат]

StackOverflow https://stackoverflow.com/questions/1916515

  •  20-09-2019
  •  | 
  •  

Вопрос

Возможный дубликат:
Оборонительное программирование

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

Некоторые считали, что необходима только проверка нулевого указателя.Я задался вопросом, можно ли проверять его на более высоком уровне, а не в каждом методе, через который он проходит, и что проверка на значение null была очень ограниченной проверкой, если объект на другом конце точки не соответствовал определенным требованиям.

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

Каков ваш опыт по этому вопросу?Как прописать в коде защиту для параметров, передаваемых подчиненным методам?

Это было полезно?

Решение

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

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

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

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

Другие советы

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

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

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

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

Я не могу придумать другого основного языка, который позволяет вам поймать столько ошибок во время компиляции, сколько и C ++. Используйте эту возможность.

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

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

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

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

Редактировать ясность: некоторые другие ответы говорят о модульных тестах. Я твердо верю, что тестовый код иногда бывает более Ценный, чем код, который он тестирует (в зависимости от того, кто измеряет значение). Тем не менее, я также думаю, что тесты единиц также необходимо, но недостаточно для защитного кодирования.

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

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

defensiveMethod(thirdPartySearch("Nothing matches me")); 
// You just passed a null to your own code.

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

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

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

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

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

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

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

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

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

гораздо лучше!

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

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

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

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

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

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

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

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

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

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

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

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

Вы можете пропустить проверки с нулевым указателем, если код находится в частном интерфейсе, которым у вас есть полный контроль, и/или вы проверяете на наличие NULL, выполнив модульный тест или некоторый тест на сборку отладки (например, ASSERT)

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

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

Вы можете создать интеллектуальный указатель, который выполняет по умолчанию 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;
}

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top