Читаем Программирование. Принципы и практика использования C++ Исправленное издание полностью

  Это так называемое “приведение в стиле языка С”, или “приведение в старом стиле”. Его любят люди, не умеющие набирать тексты (за лаконичность) и ленивые (потому что они не обязаны знать, что нужно для того, чтобы из переменной v получилась переменная типа T). С другой стороны, этот стиль яростно отвергают программисты, занимающиеся сопровождением программ, поскольку такие преобразования остаются практически незаметными и никак не привлекают к себе внимания. Приведения в языке С++ (приведения в новом стиле (new-style casts), или приведения в шаблонном стиле (template-style casts); см. раздел А.5.7) осуществляют явное преобразование типов, которое легко заметить. В языке С у вас нет выбора.

int* p = (int*)7;   /* интерпретирует битовую комбинацию:

                       reinterpret_cast(7) */

int x = (int)7.5;   /* усекает переменную типа: static_cast(7.5) */

typedef struct S1 { /* ... */ } S1;

typedef struct S2 { /* ... */ } S2;

S1 a;

const S2 b;         /* в языке С допускаются неинициализированные

                    /* константы */

S1* p = (S2*)&a    /* интерпретирует битовую комбинацию:

                       reinterpret_cast(&a) */

S2* q = (S2*)&b    /* отбрасывает спецификатор const:

                       const_cast(&b) */

S1* r = (S1*)&b    /* удаляет спецификатор const и изменяет тип;

                       похоже на ошибку */

Мы не рекомендуем использовать макросы даже в программах на языке C (раздел 27.8), но, возможно, описанные выше идеи можно было бы выразить следующим образом:

#define REINTERPRET_CAST(T,v) ((T)(v))

#define CONST_CAST(T,v) ((T)(v))

S1* p = REINTERPRET_CAST (S1*,&a);

S2* q = CONST_CAST(S2*,&b);

Это не обеспечит проверку типов при выполнении операторов reinterpret_cast и const_cast, но сделает эти ужасные операции заметными и привлечет внимание программиста. 

<p id="AutBody_Root547"><strong>27.3.5. Преобразование указателей типа void*</strong></p>

В языке указатель типа void* можно использовать как в правой части оператора присваивания, так и для инициализации указателей любого типа; в языке C++ это невозможно. Рассмотрим пример.

void* alloc(size_t x); /* выделяет x байтов */

void f (int n)

{

  int* p = alloc(n*sizeof(int)); /* OK в языке C;

                                    ошибка в языке C++ */

  /* ... */

}

Здесь указатель типа void* возвращается как результат функции alloc и неявно преобразовывается в указатель типа int*. В языке C++ мы могли бы переписать эту строку следующим образом:

int* p = (int*)alloc(n*sizeof(int)); /* OK и в языке C,

                                        и в языке C++ */

Мы использовали приведение в стиле языка C (раздел 27.3.4), чтобы оно оказалось допустимым как в программах на языке C, так и в программах на языке C++.

  Почему неявное преобразование void* в T* является недопустимым в языке С++? Потому, что такие преобразования могут быть небезопасными.

void f

{

  char i = 0;

  char j = 0;

  char* p = &i

  void* q = p;

  int* pp = q; /* небезопасно; разрешено в языке C,

                  ошибка в языке C++ */

  *pp = –1;    /* перезаписываем память, начиная с адреса &i */

В данном случае мы даже не уверены, какой фрагмент памяти будет перезаписан: переменная j или часть памяти, на которую ссылается указатель p? А может быть, память, использованная для управлении вызовом функции f (стек функции f)? Какие бы данные ни были перезаписаны, вызов функции f приведет к печальным последствиям.

Обратите внимание на то, что (обратное) преобразование указателя типа T* в указатель типа void* является совершенно безопасным, — вы не сможете придумать ужасные примеры, подобные предыдущему, — и они допускаются как в языке C, так и в языке C++.

К сожалению, неявное преобразование void* в T* широко распространено в языке C и, вероятно, является основной проблемой совместимости языков С и С++ в реальных программах (см. раздел 27.4).

<p id="AutBody_Root548"><strong>27.3.6. Перечисление</strong></p>

В языке C можно присваивать целое число перечислению без приведения int в enum. Рассмотрим пример.

enum color { red, blue, green };

Перейти на страницу:

Похожие книги

97 этюдов для архитекторов программных систем
97 этюдов для архитекторов программных систем

Успешная карьера архитектора программного обеспечения требует хорошего владения как технической, так и деловой сторонами вопросов, связанных с проектированием архитектуры. В этой необычной книге ведущие архитекторы ПО со всего света обсуждают важные принципы разработки, выходящие далеко за пределы чисто технических вопросов.?Архитектор ПО выполняет роль посредника между командой разработчиков и бизнес-руководством компании, поэтому чтобы добиться успеха в этой профессии, необходимо не только овладеть различными технологиями, но и обеспечить работу над проектом в соответствии с бизнес-целями. В книге более 50 архитекторов рассказывают о том, что считают самым важным в своей работе, дают советы, как организовать общение с другими участниками проекта, как снизить сложность архитектуры, как оказывать поддержку разработчикам. Они щедро делятся множеством полезных идей и приемов, которые вынесли из своего многолетнего опыта. Авторы надеются, что книга станет источником вдохновения и руководством к действию для многих профессиональных программистов.

Билл де Ора , Майкл Хайгард , Нил Форд

Программирование, программы, базы данных / Базы данных / Программирование / Книги по IT