Читать «Стандарты программирования на С++. 101 правило и рекомендация» онлайн - страница 147

Андрей Александреску

void TooCoolToUseNewCasts(EmployeeID id) {

 Secretary* pSecretary = (Secretary*)id; // Плохо:

 // ...                                  // преобразование в стиле С

}

При использовании старой инструкции typedef преобразование в стиле С выполняет static_cast, при новой будет выполнено reinterpret_cast с некоторым целым числом, что даст нам неопределенное поведение программы (см. рекомендацию 92).

Преобразования в стиле С++ проще искать в исходных текстах при помощи автоматического инструментария наподобие grep (но никакое регулярное выражение grep не позволит выловить синтаксис преобразования типов в стиле С). Поскольку преобразования очень опасны (в особенности static_cast для указателей и reinterpret_cast; см. рекомендацию 92), использование автоматизированного инструментария для их отслеживания — неплохая идея.

Ссылки

[Dewhurst03] §40 • [Meyers96] §2 • [Stroustrup00] §15.4.5 • [Sutter00] §44

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 способно внести в этот механизм только хаос.