в чем смысл void **?

Когда я разрабатываю в COM, я всегда вижу (void **) преобразование типов, как показано ниже.

QueryInterface(/* [in] */ REFIID riid,/* [out] */ void** ppInterface)

Что именно это означает?

ИМХО, он говорит компилятору не применять проверку типа, поскольку тип, на который указывает ppInterface, не известен клиентскому коду во время компиляции.

Спасибо ~~ ~

Обновление 1

Я так понимаю:

void * p подразумевает AnyType * p

void ** pp подразумевает указатель на AnyType *

Обновление 2

Если void ** pp означает «указатель на void *», тогда какие проверки выполняет компилятор, когда он его видит?

11
задан smwikipedia 30 August 2010 в 09:02
поделиться

9 ответов

Передача с помощью void * также гарантирует, что указанный объект не может быть удален или подделан (случайно).

«Это означает, что объект не может быть удален с помощью указателя типа void *, потому что нет объектов типа void».

1
ответ дан 3 December 2019 в 01:24
поделиться

Это позволяет API чтобы указать, что указатель может использоваться как параметр [in-out] в будущем, но на данный момент указатель не используется. (Обычно обязательным значением является NULL.)

При возврате одного из многих возможных типов без общего супертипа (например, с QueryInterface), возвращение void * действительно является единственным вариантом, и, поскольку это необходимо передать как Параметр [out] необходим указатель на этот тип (void **).

0
ответ дан 3 December 2019 в 01:24
поделиться

не применять проверку типа

Действительно, void * или void ** существуют, чтобы разрешить использование разных типов указателей, которые могут быть понижены до ] void * , чтобы соответствовать типу параметров функции.

0
ответ дан 3 December 2019 в 01:24
поделиться

Это указатель на указатель интерфейса, который вы запрашиваете с помощью этого вызова. Очевидно, вы можете запрашивать все виды интерфейсов, поэтому это должен быть указатель void. Если интерфейс не существует, указатель устанавливается в NULL.

редактировать: Подробную информацию можно найти здесь: http://msdn.microsoft.com/en-us/library/ms682521 (VS.85) .aspx

0
ответ дан 3 December 2019 в 01:24
поделиться

Это просто указатель на void * .

Например:

Something* foo;
Bar((void**)&foo);

// now foo points to something meaningful

Изменить: Возможная реализация на C #.

  struct Foo { }

  static Foo foo = new Foo();

  unsafe static void Main(string[] args)
  {
    Foo* foo;

    Bar((void**)&foo);
  }

  static unsafe void Bar(void** v)
  {
    fixed (Foo* f = &foo)
    {
      *v = f;
    }
  }
5
ответ дан 3 December 2019 в 01:24
поделиться

A void ** - указатель на void * . Это можно использовать для передачи адреса переменной void * , которая будет использоваться в качестве выходного параметра - например:

void alloc_two(int n, void **a, void **b)
{
    *a = malloc(n * 100);
    *b = malloc(n * 200);
}

/* ... */

void *x;
void *y;

alloc_two(10, &x, &y);
17
ответ дан 3 December 2019 в 01:24
поделиться

Указатель на указатель неизвестного интерфейса, который может быть предоставлен.

0
ответ дан 3 December 2019 в 01:24
поделиться

Вместо использования указателей на указатели попробуйте использовать ссылку на указатель. Это немного больше C ++, чем использование **.

например.

void Initialise(MyType &*pType)
{
    pType = new MyType();
}
0
ответ дан 3 December 2019 в 01:24
поделиться

Причина, по которой COM использует void** с QueryInterface, несколько особенная. (См. ниже.)

Вообще, void** означает просто указатель на void*, и он может использоваться для out-параметров, т.е. параметров, указывающих место, куда функция может вернуть значение. Ваш комментарий /* [out] */ указывает на то, что место, на которое указывает ppvInterface, будет записано.

"Почему параметры с типом указателя могут использоваться как параметры out?", спросите вы? Помните, что с помощью переменной-указателя можно изменить две вещи:

  1. Вы можете изменить сам указатель так, чтобы он указывал на другой объект. (ptr = ... )
  2. Вы можете изменить объект-указатель. (*ptr = ... )

Указатели передаются в функцию по значению, т.е. функция получает свою локальную копию исходного указателя, который был ей передан. Это означает, что вы можете изменить параметр указателя внутри функции (1), не затрагивая исходный указатель, поскольку изменяется только его локальная копия. Однако вы можете изменить указатель на объект (2), и это будет видно вне функции, поскольку копия имеет то же значение, что и исходный указатель, и, следовательно, ссылается на тот же объект.

Теперь конкретно о COM:

  • Указатель на интерфейс (заданный riid) будет возвращен в переменную, на которую ссылается ppvInterface. QueryInterface достигает этого с помощью механизма (2), упомянутого выше.

  • При использовании void**, один * необходим для обеспечения механизма (2); другой * отражает тот факт, что QueryInterface возвращает не вновь созданный объект (IUnknown), а уже существующий: Чтобы избежать дублирования этого объекта, возвращается указатель на этот объект (IUnknown*).

  • Если вы спрашиваете, почему ppvInterface имеет тип void**, а не IUnknown**, что казалось бы более разумным с точки зрения безопасности типов (поскольку все интерфейсы должны происходить от IUnknown), то прочитайте следующий аргумент, взятый из книги Essential COM Дона Бокса, стр. 60 (глава Type Coercion and IUnknown):


Еще одна тонкость, связанная с QueryInterface, касается его второго параметра, который имеет тип void **. Очень иронично, что QueryInterface, лежащий в основе системы типов COM, имеет довольно небезопасный с точки зрения типов прототип в C++ [...]

 IPug *pPug = 0;
 hr = punk->QueryInterface(IID_IPug, (void**)&pPug);

К сожалению, для компилятора C++ одинаково корректно выглядит следующее:

 IPug *pPug = 0;
 hr = punk->QueryInterface(IID_ICat, (void**)&pPug);

Эта более тонкая вариация также компилируется правильно:

 IPug *pPug = 0;
 hr = punk->QueryInterface(IID_ICat, (void**)pPug);

Учитывая, что правила наследования не применяются к указателям, это альтернативное определение QueryInterface не снимает проблему:

 HRESULT QueryInterface(REFIID riid, IUnknown** ppv);

К ссылкам применимо то же ограничение, что и к указателям. Следующее альтернативное определение, возможно, более удобно для использования клиентами:

 HRESULT QueryInterface(const IID& riid, void* ppv);

[...] К сожалению, это решение не уменьшает количество ошибок [...] и, устраняя необходимость приведения, устраняет визуальный индикатор того, что безопасность типов C++ может быть под угрозой. Учитывая желаемую семантику QueryInterface, типы аргументов, выбранные Microsoft, являются разумными, если не безопасными для типов или элегантными. [...]

18
ответ дан 3 December 2019 в 01:24
поделиться
Другие вопросы по тегам:

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