Почему здесь используется 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* вместо. Некоторые мысли

  1. Handle<T> имеет T* -> тег (не T) - (каким-то образом) -> реальный объект (не типа T)

  2. Тег имеет размер void*, И пользователю не нужно знать о реальном типе тега.

  3. Два тега равны - они относятся к одному и тому же объекту.

  4. (резюме, с точки зрения пользователя) Handle<T> имеет void** -> void*

Итак, оригинал bool operator==(Handle<S> that) делает то, что должен делать: сравнивать значения тегов. Но сначала проверьте указатели.

Другие вопросы по тегам