Читать «Стандарты программирования на С++. 101 правило и рекомендация» онлайн - страница 147
Андрей Александреску
void TooCoolToUseNewCasts(EmployeeID id) {
Secretary* pSecretary = (Secretary*)id; // Плохо:
// ... // преобразование в стиле С
}
При использовании старой инструкции typedef
преобразование в стиле С выполняет static_cast
, при новой будет выполнено reinterpret_cast
с некоторым целым числом, что даст нам неопределенное поведение программы (см. рекомендацию 92).
Преобразования в стиле С++ проще искать в исходных текстах при помощи автоматического инструментария наподобие grep
(но никакое регулярное выражение grep
не позволит выловить синтаксис преобразования типов в стиле С). Поскольку преобразования очень опасны (в особенности static_cast
для указателей и reinterpret_cast
; см. рекомендацию 92), использование автоматизированного инструментария для их отслеживания — неплохая идея.
Ссылки
96. Не применяйте memcpy
или memcmp
к не-POD типам
Резюме
Не работайте рентгеновским аппаратом (см. рекомендацию 91). Не используйте memcpy
и memcmp
для копирования или сравнения чего-либо структурированного более, чем обычная память.
Обсуждение
Функции memcpy
и memcmp
нарушают систему типов. Использовать memcpy
для копирования объектов — это то же, что использовать ксерокс для копирования денег, а сравнивать объекты при помощи memcmp
— то же, что сравнивать двух леопардов по количеству пятен. Инструменты и методы могут казаться подходящими для выполнения работы, но они слишком грубы для того, чтобы сделать ее правильно.
Объекты С++ предназначены для сокрытия данных (возможно, наиболее важный принцип в разработке программного обеспечения; см. рекомендацию 11). Объекты скрывают данные (см. рекомендацию 41) и предоставляют точные абстракции для копирования этих данных посредством конструкторов и операторов присваивания (см. рекомендации с 52 по 55). Пройтись по ним грубым инструментом типа memcpy
— серьезное нарушение принципа сокрытия информации, которое зачастую приводит к утечкам памяти и ресурсов (в лучшем случае), аварийному завершению программы (в случае похуже) или неопределенному поведению (в самом худшем случае). Например:
{
// Создаем два int в памяти
shared_ptr<int> p1(new int), p2(new int);
memcpy(&p1, &p2, sizeof(p1)); // Так делать нельзя!!!
} // Утечка памяти: p2 никогда не удаляется
// повреждение памяти: p1 удаляется дважды
Неверное применение memcpy
может влиять на такие фундаментальные свойства, как тип и сущность объекта. Компиляторы часто добавляют к полиморфным объектам скрытые данные (так называемый указатель на виртуальную таблицу), которые определяют сущность объекта во время выполнения программы. В случае множественного наследования в объекте содержится несколько таких таблиц, с различными смещениями внутри объекта, и большинство реализаций добавляют дополнительные внутренние указатели при виртуальном наследовании. При обычном использовании компилятор принимает меры для корректного управления всеми скрытыми полями; применение memcpy
способно внести в этот механизм только хаос.