Ответ 1
В C преобразования указателей в и из void*
всегда были неявными.
В С++ преобразования от T*
до void*
являются неявными, но void*
для чего-либо еще требует отбрасывания.
Я пытаюсь понять различия между C и С++ в отношении указателей void. следующие компиляции в C, но не С++ (все компиляции, выполненные с помощью gcc/g++ -ansi -pedantic -Wall):
int* p = malloc(sizeof(int));
Потому что malloc
возвращает void*
, который С++ не позволяет назначать int*
, в то время как C разрешает это.
Однако здесь:
void foo(void* vptr)
{
}
int main()
{
int* p = (int*) malloc(sizeof(int));
foo(p);
return 0;
}
Оба С++ и C скомпилируют его без жалоб. Почему?
K & R2 говорят:
Любой указатель на объект может быть преобразован в тип
void *
без потерь информации. Если результат преобразован обратно в исходный указатель тип, исходный указатель выздоровел.
И это довольно суммирует все, что есть о void*
преобразованиях в C. Что диктует стандарт С++?
В C преобразования указателей в и из void*
всегда были неявными.
В С++ преобразования от T*
до void*
являются неявными, но void*
для чего-либо еще требует отбрасывания.
С++ более строго типизирован, чем C. Многие преобразования, особенно те, которые подразумевают различную интерпретацию значения, требуют явного преобразования. Новый оператор в С++ - это безопасный тип размещения памяти в куче без явного приведения.
Полезно понять, что преобразования типа указателя на самом деле не требуют выполнения дополнительных инструкций ЦП. Они анализируются во время компиляции, чтобы понять намерения разработчика. void *
- непрозрачный указатель. Все это говорит о том, что тип заостренного объекта неизвестен. C слабо типизирован. Это позволяет прямое преобразование между (void *
) и любым (T*
) неявным образом. С++ строго типизирован. Преобразование из (void *
) в (T*
) не будет действительно хорошим примером для строго типизированного языка. Но С++ должен был оставаться обратно совместимым с C, поэтому он должен был допускать такие преобразования. Тогда руководящий принцип: явный лучше, чем неявный. Следовательно, если вы хотите преобразовать (void*
) в какой-то конкретный (T*
) указатель, вам нужно явно написать это в коде. Преобразование из (T*
) в (void*
) не требует явного преобразования, так как нет ничего, что можно было бы сделать с прямым указателем (void *) (можно вызвать free(), хотя). Следовательно, преобразование (T*
) в (void*
) в значительной степени безопасно.