Подтвердить что ты не робот

При преобразовании указателя void в указатель определенного типа, который лучше всего использует символ casting, static_cast или reinterpret_cast?

Возможный дубликат:
Должен ли я использовать static_cast или reinterpret_cast при запуске void * для любого

В этой программе у меня есть void * в качестве параметра и вы хотите передать его определенному типу. Но я не знаю, какой "литой символ" использовать. Работают как static_cast, так и reinterpret_cast. Какая из них лучше? Что рекомендует стандартный С++?

typedef struct
{
    int a;
}A, *PA;

int foo(void* a)                // the real type of a is A*
{
    A* pA = static_cast<A*>(a); // or A* pA = reinterpret_cast<A*>(a);?
    return pA->a;
}

Здесь

A* pA = static_cast<A*>(a);

или

A* pA = reinterpret_cast<A*>(a);

более верно?

4b9b3361

Ответ 1

static_cast более подходит для преобразования void* в указатель другого типа.

static_cast - выбор, когда есть естественное, интуитивное преобразование между двумя типами, которое не обязательно гарантируется для работы во время выполнения. Например, вы можете использовать static_cast для преобразования указателей базового класса в указатели на производные классы, что является конверсией, которая имеет смысл в некоторых случаях, но не может быть проверена до выполнения. Аналогично, вы можете использовать static_cast для преобразования из int в char, который хорошо определен, но может привести к потере точности при выполнении.

reinterpret_cast, с другой стороны, является оператором литья, предназначенным для совершения конверсий, которые в принципе небезопасны или не переносимы. Например, вы можете использовать reinterpret_cast для преобразования из void * в int, который будет работать правильно, если ваша система имеет sizeof (void*) & le; sizeof (int). Вы также можете использовать reinterpret_cast для преобразования a float* в int* или наоборот, который является специфичным для платформы, поскольку конкретные представления float и int не гарантируют что-либо общее с друг с другом.

Короче говоря, если вы когда-либо обнаруживаете, что делаете конверсию, в которой листинг является логически значимым, но может не обязательно выполняться во время выполнения, избегайте reinterpret_cast. static_cast - хороший выбор, если у вас есть некоторые предварительные знания о том, что бросок будет работать во время выполнения, и связывается с компилятором "Я знаю, что это может не сработать, но по крайней мере это имеет смысл, и у меня есть причина поверить он будет правильно делать правильные вещи во время выполнения". Затем компилятор может проверить, что бросок находится между связанными типами, сообщая об ошибке времени компиляции, если это не так. Использование reinterpret_cast для этого с помощью конверсий указателей полностью исключает проверку безопасности компиляции.

Есть несколько обстоятельств, когда вы можете использовать dynamic_cast вместо static_cast, но в основном это касается приведения в иерархии классов и (редко) непосредственно относятся к void*.

Что касается того, какой из них предпочтительнее spec, ни один из них не упоминается как "правильный для использования" (или, по крайней мере, я не помню, чтобы один из них упоминался таким образом.) Однако, я думаю, спецификация хочет использовать static_cast над reinterpret_cast. Например, при использовании C-стиля, как в

A* ptr = (A*) myVoidPointer;

Порядок операторов литья, которые пытались всегда пытаться использовать static_cast до reinterpret_cast, а именно поведение, которое вы хотите с reinterpret_cast, не гарантированно переносится.

Ответ 2

Используйте для этого static_cast. Только в редких случаях, когда нет другого способа, используйте reinterpret_cast.

Ответ 3

Если вы выполняете иерархию классов, используйте dynamic_cast - он проверяет, совместим ли фактический объект с типом, который вы выполняете.

Ответ 4

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

Ответ 5

Ни. Почему вы передаете указатель на пустоту в первую очередь? В C он довольно распространен из-за отсутствия альтернатив, но на С++ почти нет причин делать что-либо в этом роде.

Изменить: @Dan O повышает вероятность использования броска, чтобы свести к минимуму сгенерированный код. Для тех, кто не совсем понимает, о чем он говорит, рассмотрите что-то вроде std::vector - когда вы определяете (например) std::vector<A *> и std::vector<B *>, вы обычно будете иметь совершенно отдельный код для каждого хотя оба они просто сохраняют указатели, поэтому они могут использовать один и тот же код. Это можно обойти, определив контейнер, который содержит только void * s, и вернет правильный тип при извлечении элемента из контейнера.

Прежде всего, это, как правило, преждевременная оптимизация в лучшем случае (и часто может оказаться пессимизацией). Тем не менее, временами это имеет смысл. Однако, если это так, вы хотите ограничить листинг в одном месте, но создайте шаблонный интерфейс для псевдо-универсального контейнера void * s:

template <class T>
pointer_container {
    std::vector <void *> data;
public:
    T *operator[](size_t index) { return static_cast<T *>(data[index]); }
};

Теперь, конечно, верно, что теперь у нас есть актерский состав. И да, чтобы дать прямой ответ на исходный вопрос, a static_cast является правильным для использования в подобной ситуации.

В дни MS-DOS (для примера) такой код был довольно распространенным. Однако сегодня этот стиль кода совершенно бессмысленен.

Теперь OP сказал, что он застрял с фиксированным интерфейсом, и это его работа по его реализации. Я не знаю, где он работает или с кем он работает, и я не видел остальную часть кода, с которым это работает, поэтому я не могу точно сказать, что то, что они делают, плохо спроектировано и должны быть изменены. OTOH, исходя из моего прошлого опыта, я бы предположил, что вероятность того, что это дело, составляет не менее 99%. Таким образом, я поддерживаю свой первоначальный ответ. Ответы, опубликованные, которые только что сказали "static_cast", ничего об избегающем акте - это те, которые действительно бесполезны. В то время как едва ли возможно, что бросок действительно нельзя избежать, просто предполагая, что он оправдан, является неправильной реакцией и плохим ответом.

Я пойду дальше: если вопрос о кастинге, который не дает доказательств о том, почему это приведение неизбежно, единственный совет, который кто-либо должен считать "хорошим" вообще, - это советы по ликвидации целиком. Это может (в редких случаях) оказаться в том, что броска нельзя избежать - но это, безусловно, исключение, а не правило.

Ответ 6

reinterpret_cast принудительно преобразует void* в целевой тип данных. Это не гарантирует никакой безопасности, и ваша программа может быть повреждена, поскольку базовый объект может быть чем угодно.

Для примера, вы можете придать myclass* to void*, а затем использовать reinterpret_cast, чтобы преобразовать его в yourclass*, который может иметь совершенно другой формат.

Поэтому его лучше и рекомендуется использовать static_cast