С++:Безопасный способ приведения целого числа к указателю

StackOverflow https://stackoverflow.com/questions/1831991

Вопрос

Мне нужно преобразовать целочисленный тип, содержащий адрес, в фактический тип указателя.Я мог бы использовать reinterpret_cast следующим образом:

MyClass *mc1 = reinterpret_cast<MyClass*>(the_integer);

Однако при этом не выполняются какие-либо проверки во время выполнения, чтобы определить, действительно ли рассматриваемый адрес содержит объект MyClass.Я хочу знать, есть ли какая-либо польза от первого преобразования в void* (с использованием reinterpret_cast), а затем использования динамического_cast для результата.Так:

void *p = reinterpret_cast<void*>(the_integer);
MyClass *mc1 = dynamic_cast<MyClass*>(p);
assert(mc1 != NULL);

Есть ли преимущество в использовании второго метода?

Это было полезно?

Решение

Тип проверки включен dynamic_cast реализуется по-разному в разных реализациях C++;если вам нужен ответ для вашей конкретной реализации, вам следует указать, какую реализацию вы используете.Единственный способ ответить на этот вопрос в целом — обратиться к стандарту ISO C++.

Судя по моему чтению стандарта, вызов dynamic_cast на указателе void является незаконным:

dynamic_cast<T>(v)

«Если T является типом указателя, v должно быть значением указателя на завершенный тип класса»

(из версии 5.2.7.2 стандарта ISO C++). void не является полным типом класса, поэтому выражение является недопустимым.

Интересно, какой тип приводится к может быть пустым указателем, т.е.

void * foo = dynamic_cast<void *>(some_pointer);

В этом случае dynamic_cast всегда завершается успешно, а результирующее значение является указателем на наиболее производный объект, на который указывает v.

Другие советы

Нет, особого преимущества в этом нет.В тот момент, когда вы используете reinterpret_cast, все ставки сделаны.Вы должны убедиться, что актерский состав действителен.

На самом деле серьезного преимущества нет.Если void* указывает на что-то, что не является указателем на полиморфный объект, вы сразу же столкнетесь с неопределенным поведением (обычно нарушением прав доступа).

Безопасный способ — вести учет всех живых объектов MyClass.Лучше всего хранить эту запись в std::set<void*>, что означает, что вы можете легко добавлять, удалять и тестировать элементы.

Причина хранения их как void*Дело в том, что вы не рискуете сделать что-то неприятное, например, создавая невыровненные MyClass* указатели из ваших целых чисел.

  • Прежде всего «переосмысление» int к void * это плохая идея.Если sizeof(int) это 4 и sizeof(void *) равно 8 (система 64x), это неправильно.

  • Более того dynamic_cast справедливо только для случая полиморфных классов.

Вариант 1 — ваш единственный (полу) переносимый/действительный вариант.

Вариант 2:недопустим C++ в качестве динамического_cast (поскольку void не допускается).

На уровне реализации требуется информация о типе из исходного типа, чтобы добраться до целевого типа.Невозможно (или может не быть) получить информацию о типе источника среды выполнения из void*, поэтому это также недопустимо.

Dynamic_Cast используется для преобразования вверх и вниз по иерархии типов, а не из неизвестных типов.

В качестве примечания вам, вероятно, следует использовать void*, а не целое число для хранения нетипизированного указателя.Существует вероятность того, что int окажется недостаточно большим для хранения указателя.

Самый безопасный способ обработки указателей в C++ — это их типобезопасность.Это означает:

  • Никогда не храните указатели ни в чем другом, кроме указателя.
  • Избегайте пустых указателей
  • Никогда не передавайте указатели на другие процессы
  • рассмотрите слабый_ptr, если вы планируете использовать указатели на потоках

Причина этого:то, что вы планируете сделать, небезопасно, и его можно избежать, если только вы не взаимодействуете с небезопасным (устаревшим?) кодом.В этом случае рассмотрите ответ MSalters, но имейте в виду, что это по-прежнему является проблемой.

Если ты точно знаешь, что the_integer указывает на известный базовый класс (который имеет хотя бы один виртуальный член), на самом деле это может быть преимуществом:зная, что объект принадлежит к определенному производному классу.Но тебе придется reinterpret_cast сначала в свой базовый класс, а затем выполните dynamic_cast:

BaseClass* obj = reinterpret_cast<BaseClass*>(the_integer);
MyClass* myObj = dynamic_cast<BaseClass*>(obj);

Используя void* в dynamic_cast бесполезно и просто неправильно.Вы не можете использовать dynamic_cast чтобы проверить, существует ли действительный объект в произвольном месте памяти.

Вам также следует обратить внимание при сохранении адресов в переменных типа без указателя.Существуют архитектуры, в которых sizeof(void*) != sizeof(int), например.ЛП64.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top