C ++: безопасный способ приведения целого к указателю - PullRequest
1 голос
/ 02 декабря 2009

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

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

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

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

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

Ответы [ 8 ]

2 голосов
/ 02 декабря 2009

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

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

dynamic_cast<T>(v)

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

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

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

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

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

2 голосов
/ 02 декабря 2009

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

2 голосов
/ 02 декабря 2009

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

1 голос
/ 02 декабря 2009

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

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

0 голосов
/ 02 декабря 2009

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

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

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

0 голосов
/ 02 декабря 2009

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

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

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

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

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

0 голосов
/ 02 декабря 2009

Если вы точно знаете, что 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), например LP64.

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

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

...