Программирование >>  Перегруженные имена функций и идентификаторы 

1 ... 169 170 171 [ 172 ] 173 174 175 ... 210


Как встроенные функции могут влиять на соотношение безопасности и скорости?

В обычном С вы можете получить инкапсулированные структуры , помещая в них указатель на 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()



1 ... 169 170 171 [ 172 ] 173 174 175 ... 210

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика