|
Программирование >> Перегруженные имена функций и идентификаторы
Как встроенные функции могут влиять на соотношение безопасности и скорости? В обычном С вы можете получить инкапсулированные структуры , помещая в них указатель на void, и заставляя его указывать на настоящие данные, тип которых неизвестен пользователям структуры. Таким образом, пользователи не знают, как интерпретировать эти данные, а функции доступа преобразуют указатель на void к нужному скрытому типу. Так достигается некоторый уровень инкапсуляции. К сожалению, этот метод идет вразрез с безопасностью типов, а также требует вызова функции для доступа к любым полям структуры (если вы позволили бы прямой доступ, то его мог бы получить кто угодно, поскольку будет известно, как интерпретировать данные, на которые указывает void*. Такое поведение со стороны пользователя приведет к сложностям при последующем изменении структуры подлежащих данных). Стоимость вызова функции невелика, но дает некоторую прибавку. Классы С++ позволяют встраивание функций, что дает вам безопасность инкапсуляции вместе со скоростью прямого доступа. Более того, типы параметры встраиваемых функций проверяются компилятором, что является преимуществом по сравнению с сишными #define макросами. Зачем мне использовать встроенные функции? Почему не использовать просто #define макросы? Поскольку #define макросы опасны. В отличие от #define макросов, встроенные (inline) функции не подвержены известным ошибкам двойного вычисления, поскольку каждый аргумент встроенной функции вычисляется только один раз. Другими словами, вызов встроенной функции - это то же самое что и вызов обычной функции, только быстрее: Макрос, возвращающий модуль (абсолютное значение) i #define unsafe(i) \ ( (i) >= 0 ? (i) : -(i) ) Встроенная функция, возвращающая абсолютное значение i inline int safe(int i) return i >= 0 ? i : -i; int f(); void userCode(int x) int ans; ans = unsafe(x++); Ошибка! x инкрементируется дважды ans = unsafe(f()); Опасно! f() вызывается дважды ans = safe(x++); Верно! x инкрементируется один раз ans = safe(f()); Верно! f() вызывается один раз Также, в отличие от макросов, типы аргументов встроенных функций проверяются, и выполняются все необходимые преобразования. Макросы вредны для здоровья; не используйте их, если это не необходимо. Что такое ошибка в порядке статической инициализации ( static initialization order fiasco )? Незаметный и коварный способ убить ваш проект. Ошибка порядка статической инициализации - это очень тонкий и часто неверно воспринимаемый аспект С++. К сожалению, подобную ошибку очень сложно отловить, поскольку она происходит до вхождения в функцию main(). Представьте себе, что у вас есть два статических объекта x и y, которые находятся в двух разных исходных файлах, скажем x.cpp и y.cpp. И путь конструктор объекта y вызывает какой-либо метод объекта x. Вот и все. Так просто. Проблема в том, что у вас ровно пятидесятипроцентная возможность катастрофы. Если случится, что единица трансляции с x.cpp будет проинициализирована первой, то все в порядке. Если же первой будет проинициализирована единица трансляции файла y.cpp, тогда конструктор объекта y будет запущен до конструктора x, и вам крышка. Т.е., конструктор y вызовет метод объекта x, когда сам x еще не создан. Примечание: ошибки статической инициализации не распространяются на базовые/встроенные типы, такие как int или char*. Например, если вы создаете статическую переменную типа float, у вас не будет проблем с порядком инициализации. Проблема возникает только тогда, когда у вашего статического или глобального объекта есть конструктор. Как предотвратить ошибку в порядке статической инициализации? Используйте создание при первом использовании , то есть, поместите ваш статический объект в функцию. Представьте себе, что у нас есть два класса Fred и Barney. Есть глобальный объект типа Fred, с именем x, и глобальный объект типа Barney, с именем y. Конструктор Barney вызывает метод goBowling() объекта x. Файл x.cpp содержит определение объекта x: File x.cpp #include Fred.hpp Fred x; Файл y.cpp содержит определение объекта y: File y.cpp #include Barney.hpp Barney y; Для полноты представим, что конструктор Barney::Barney() выглядит следующим образом: File Barney.cpp #include Barney.hpp Barney::Barney() ... x.goBowling(); ... Проблема случается, если y создается раньше, чем x, что происходит в 50% случаев, поскольку x и y находятся в разных исходных файлах. Есть много решений для этой проблемы, но одно очень простое и переносимое - заменить глобальный объект Fred x, глобальной функцией x(), которая возвращает объект типа Fred по ссылке. File x.cpp #include Fred.hpp Fred& x()
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |