Почему здесь используется void**?
Код забрать с v8-0.2.5
/**
* Checks whether two handles are the same.
* Returns true if both are empty, or if the objects
* to which they refer are identical.
* The handles' references are not checked.
*/
template <class S> bool operator==(Handle<S> that) {
void** a = reinterpret_cast<void**>(**this);
void** b = reinterpret_cast<void**>(*that);
if (a == 0) return b == 0;
if (b == 0) return false;
return *a == *b;
}
Handle
Оператор перегрузки *, так что **this
а также *that
тип возврата T*
,
Ну, это похоже
void* a = reinterpret_cast<void*>(**this);
void* b = reinterpret_cast<void*>(*that);
return a == b;
тоже будет хорошо работать?
3 ответа
Во-первых, я восхищаюсь, что я читаю код в ссылке по диагонали. Видимо, класс Handle перегружает оператор разыменования (*
) вернуть T*
это обрабатывается. Таким образом, выражения в первой строке означают следующее:
this
является (возможно, cv-квалифицированным)Handle<T> * const
,*this
являетсяHandle<T> &
**this
это возвращаемое значение дескриптораoperator*
, какойT*
ты упомянул.- Наконец то
T*
переосмысливается вvoid**
, Обратите внимание, что добавлена одна дополнительная косвенность, поэтому разыменование результата возможно и приведет кvoid* &
вместоT&
- Эквивалентная строка с
that
даетS*
это интерпретируется какvoid**
,
Таким образом, вы получаете пару указателей на разные типы T*
а также S*
которые магически переосмысливаются как void**
, Затем код выполняет нулевые проверки, а затем волшебную строку:
return *a == *b;
Который сравнивает (возможно, не выровненный!) Первым sizeof(void*)
байты объектов типов T
а также S
которые на самом деле указаны a
а также b
, Если вы не можете быть полностью уверены, что T
а также S
иметь правильный размер и выравнивание, проверка полностью поддельная. Например, проверка будет иметь смысл, если вы знаете, что T
сам по себе всегда является указателем или объектом интеллектуального указателя с одинаковым размером указателя, поэтому у вас могут быть разные маркеры, указывающие на разные объекты указателя, которые, тем не менее, указывают (на 2-м уровне косвенности) на один и тот же объект. Это позволяет GC перемещать базовый объект без необходимости обновления всех дескрипторов, просто обновляя содержимое указателей 1-го уровня.
Handle<T> has T* -----> T = U* pinned in memory -----> actual object U can be moved
Итак, чтобы ответить на ваш вопрос, приведение только к void*
(без увеличения косвенности) - это не то же самое, что проверка в коде - ваша версия будет сравнивать указатели, поэтому в моем примере два разных дескриптора одного и того же объекта могут отличаться от вашего альтернативного кода.
PS: это тоже плохой стиль - возвращать класс T*
от обоих operator*
а также operator->
потому что тогда вы нарушаете общую идентичность между p->x
а также (*p).x
, Оператор разыменования должен обычно возвращать T&
если оператор доступа члена возвращает T*
,
Если a
а также b
имел тип void*
тогда вы не могли бы разыменовать их (не приведя их сначала к чему-то другому), поэтому *a == *b
не сработает.
Хавьер Мартин прав. Вы не можете просто сравнить указатели, как вы предложили в вопросе. Прежде всего в контексте V8 V Handle<T>
имеет ограничение по типу T
, Вы не можете взять любой класс и применить к нему дескриптор. T
это просто класс фасада, с которым пользователь имеет дело: v8::String
, v8::Integer
и так далее. Объект таких типов никогда не создается, но такие классы используются как интерфейсы для внутренних устройств.
На самом деле указатель, который Handle<>
Хранилище - это указатель на что-нибудь, скажем, "Tag". У нас есть тот же объект, что два Handle<>
обратитесь, если их теги совпадают. Внутренне тег имеет размер void*
и как-то относится к реальному объекту. Пользователю не нужно знать, что такое тег Handle<>
использования void*
вместо. Некоторые мысли
Handle<T>
имеетT*
-> тег (неT
) - (каким-то образом) -> реальный объект (не типа T)Тег имеет размер
void*
, И пользователю не нужно знать о реальном типе тега.Два тега равны - они относятся к одному и тому же объекту.
(резюме, с точки зрения пользователя)
Handle<T>
имеетvoid**
->void*
Итак, оригинал bool operator==(Handle<S> that)
делает то, что должен делать: сравнивать значения тегов. Но сначала проверьте указатели.