Совместимость нулевого указателя с static_cast

Взгляните на то, как Ruby on Rails делает это.

Первый существуют так называемые файлы миграции, которые в основном преобразовывают схему базы данных и данные от версии N до версии N+1 (или в случае понижения от версии N+1 до N). База данных имеет таблицу, которая говорит текущую версию.

базы данных Test всегда вытираются чистые перед модульными тестами и заполняются с фиксированными данными из файлов.

5
задан Martin York 9 December 2009 в 10:31
поделиться

5 ответов

Какой компилятор вы используете? Статическое приведение от базового типа к производному типу может привести к корректировке указателя - особенно вероятно, если задействовано множественное наследование (что, похоже, не относится к вашей ситуации из вашего описания). Однако это все еще возможно без MI.

Стандарт указывает, что если выполняется приведение значения нулевого указателя, результатом будет значение нулевого указателя (статическое приведение 5.2.9 / 8). Однако я думаю, что на многих компиляторах большинство понижающих преобразований (особенно когда задействовано одиночное наследование) не приводят к корректировке указателя, поэтому я мог представить, что в компиляторе может быть ошибка, из-за которой он не будет выполнять специальную проверку на null, которая потребуется, чтобы избежать «преобразования» нулевого указателя с нулевым значением в какой-то бессмысленный указатель с ненулевым значением. Я предполагаю, что для существования такой ошибки вы должны делать что-то необычное, чтобы компилятор вынужден был корректировать указатель при понижающем преобразовании.

Было бы интересно посмотреть, какой тип ассемблерного кода был сгенерирован для вашего примера.

И для получения подробной информации о том, как компилятор может компоновить объект, который может нуждаться в корректировке указателя с помощью статических преобразований, Стэн Липпман «Внутри объектной модели C ++» является отличным ресурсом.

Статья Страуструпа по теме. Множественное наследование для C ++ (с 1989 г.) также является хорошим чтением. Это' Очень плохо, если в компиляторе C ++ есть ошибка, о которой я размышляю здесь - Страуструп подробно обсуждает проблему нулевого указателя в этой статье (4.5 указатели с нулевым значением).

По вашему второму вопросу:

Q2. Является ли static_casting от B до C / D / E допустимым?

Это совершенно верно до тех пор, пока вы выполняете приведение указателя B к указателю C / D / E, указатель B фактически указывает на под- объект C / D / E объекта (соответственно) и B не является виртуальной базой. Об этом говорится в том же параграфе стандарта (5.2.9 / 8 Static cast). Я выделил предложения параграфа, наиболее важные для ваших вопросов:

rvalue типа «указатель на cv1 B», где B - тип класса, может быть преобразовано в rvalue типа «указатель на cv2 D» , где D - класс, производный (раздел 10) от B, если существует допустимое стандартное преобразование из «указателя на D» в «указатель на B» (4.10), cv2 является той же квалификацией cv, что и cv1, или большей квалификацией cv, чем cv1, и B не является виртуальным базовым классом D . Значение нулевого указателя (4.10) преобразуется в значение нулевого указателя целевого типа. Если rvalue типа «указатель на cv1 B» указывает на B, который фактически является подобъектом объекта типа D, результирующий указатель указывает на включающий объект типа D. В противном случае результат преобразования не определен.

В заключение, вы можете обойти проблему, используя что-то вроде:

Set1(pEntity ? static_cast<C*>(pEntity) : 0);

, что компилятор должен делать за вас.

10) преобразуется в значение нулевого указателя целевого типа. Если rvalue типа «указатель на cv1 B» указывает на B, который фактически является подобъектом объекта типа D, результирующий указатель указывает на включающий объект типа D. В противном случае результат преобразования не определен.

В заключение, вы можете обойти проблему, используя что-то вроде:

Set1(pEntity ? static_cast<C*>(pEntity) : 0);

, что компилятор должен делать за вас.

10) преобразуется в значение нулевого указателя целевого типа. Если rvalue типа «указатель на cv1 B» указывает на B, который фактически является подобъектом объекта типа D, результирующий указатель указывает на включающий объект типа D. В противном случае результат преобразования не определен.

В заключение, вы можете обойти проблему, используя что-то вроде:

Set1(pEntity ? static_cast<C*>(pEntity) : 0);

, что компилятор должен делать за вас.

4
ответ дан 18 December 2019 в 09:50
поделиться

Вы можете static_cast нулевой указатель - он даст вам нулевой указатель.

В вашем фрагменте проблема, скорее всего, заключается в том, что вы передаете несовместимые значения ] pEntity и iMyEntityType в функцию. Таким образом, когда выполняется static_cast , он слепо приводит к неправильному типу (не к тому же типу, что и фактический объект), и вы получаете недопустимый указатель, который позже передается вниз по стеку вызовов и вызывает неопределенное поведение (сбой программа). dynamic_cast в том же случае видит, что объект действительно не ожидаемого типа, и возвращает нулевой указатель.

8
ответ дан 18 December 2019 в 09:50
поделиться

static_cast сам по себе не может вызвать сбой - его поведение во время выполнения такое же, как reinterpret_cast . В вашем коде что-то не так.

3
ответ дан 18 December 2019 в 09:50
поделиться

MyClass * p = static_cast (0) работает хорошо.

Новое :

Если вы используете множественное наследование, то static_cast может сместить указатель. Рассмотрим следующий код:

struct B1 {};
struct B2 {};

struct A : B2, B1 {
 virtual ~A() {}
};

Что такое struct A ? A содержит таблицу виртуальных функций и B1 и B2 . B1 сдвигается относительно A . Чтобы преобразовать B1 в , компилятору необходимо выполнить сдвиг назад.

Если указатель на B1 имеет значение NULL, то сдвиг дает недопустимый результат.

1
ответ дан 18 December 2019 в 09:50
поделиться

static_cast предназначен для ситуаций, когда вы знаете , что приведение может быть выполнено (либо вы приводите к родительскому классу, либо у вас есть другие способы оценки типа класс). Тип не проверяется во время выполнения (отсюда static ). С другой стороны, dynamic_cast проверяет во время выполнения, действительно ли объект относится к тому типу, к которому вы хотите его привести. Что касается reinterpret_cast , он не делает ничего, кроме использования одной и той же памяти для разных целей. Обратите внимание, что reinterpret_cast никогда не следует использовать для перехода от одного класса к другому.

В конце концов, причина сбоя static_cast при сбое NULL указателя заключается в том, что static_cast с наследованием может потребоваться немного арифметики указателя от компилятора. Это зависит от того, как компилятор на самом деле реализует наследование. Но в случае множественного наследования у него нет выбора.

Один из способов убедиться в этом состоит в том, что дочерний класс «содержит» родительский класс. Виртуальная таблица содержит одну из родительских, но с дополнительными функциями. Если функции добавлены в начале, то любое приведение к родительскому классу будет указывать на другое место ... откуда не могут быть видны функции дочернего класса. Я надеюсь, что это имеет смысл.

Замечание по арифметике указателей

Во-первых, это всегда так и для множественного наследования, но компилятор может сделать это и для одиночного наследования.

В основном, если вы посмотрите на макет памяти для содержимого объекта с помощью виртуальных методов, вы можете сделать что-то вроде:

+---------------+----------------+
| ptr to vtable | members   .... |
+---------------+----------------+

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

Теперь, если у вас есть множественное наследование, все обстоит сложнее. В частности, вы, вероятно, не сможете последовательно объединить vtables и members (по крайней мере, в общем случае). Итак, скажем, вы унаследованы от классов A, B и C, у вас, вероятно, будет что-то вроде:

                       A                       B                      C
+----------------------+-----------+-----------+----------+-----------+-----+
| local vtable/members | vtable A  | members A | vtable B | members B | ... |
+----------------------+-----------+-----------+----------+-----------+-----+

Таким образом, если вы укажете на A, вы увидите объект как объект типа A , плюс остальное. Но если вы хотите видеть объект как имеющий тип B , вам нужно указать адрес B и т. Д. Обратите внимание, что это может быть не совсем то, что делает система, но это мерзость ее.

В частности, вы, вероятно, не сможете последовательно объединить vtables и members (по крайней мере, в общем случае). Итак, скажем, вы унаследованы от классов A, B и C, у вас, вероятно, будет что-то вроде:

                       A                       B                      C
+----------------------+-----------+-----------+----------+-----------+-----+
| local vtable/members | vtable A  | members A | vtable B | members B | ... |
+----------------------+-----------+-----------+----------+-----------+-----+

Таким образом, если вы укажете на A, вы увидите объект как объект типа A , плюс остальное. Но если вы хотите видеть объект как имеющий тип B , вам нужно указать адрес B и т. Д. Обратите внимание, что это может быть не совсем то, что делает система, но это мерзость ее.

В частности, вы, вероятно, не сможете последовательно объединить vtables и members (по крайней мере, в общем случае). Итак, скажем, вы унаследованы от классов A, B и C, у вас, вероятно, будет что-то вроде:

                       A                       B                      C
+----------------------+-----------+-----------+----------+-----------+-----+
| local vtable/members | vtable A  | members A | vtable B | members B | ... |
+----------------------+-----------+-----------+----------+-----------+-----+

Таким образом, если вы укажете на A, вы увидите объект как объект типа A , плюс остальное. Но если вы хотите видеть объект как имеющий тип B , вам нужно указать адрес B и т. Д. Обратите внимание, что это может быть не совсем то, что делает система, но это мерзость ее.

1
ответ дан 18 December 2019 в 09:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: