С++:Безопасный способ приведения целого числа к указателю
-
11-09-2019 - |
Вопрос
Мне нужно преобразовать целочисленный тип, содержащий адрес, в фактический тип указателя.Я мог бы использовать 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.