- Будет ли работать приведение типов?
Нет, не в C. Запрос другого интерфейса с помощью указателя интерфейса может потребовать настройки указателя. Простое переосмысление указателя на один интерфейс как указателя на другой интерфейс в таких обстоятельствах не работает (см. ниже более подробное исследование).
В C++ это можно заставить работать, предоставив определяемую пользователем функцию преобразования , хотя он чрезвычайно хрупок и может эффектно сломаться тонкими и не очень тонкими способами.
- Будет ли работать QueryInterface?
Да. Правильным подходом является запрос другого интерфейса через указатель интерфейса. Код, который вы предоставили, правильный.
- Нужно ли мне делать что-то еще?
Нет, не совсем, если вы следуете правилам COM. Одна деталь часто упускается из виду: успешный вызов QueryInterface
увеличивает счетчик ссылок на интерфейс, так что вам придется Release
каждый интерфейс, возвращенный вызовом QueryInterface
.
Почему кастинг небезопасен
Итак, если IDerived
наследуется от IBase
, то почему очевидный выбор, преобразование указателя из IDerived*
в IBase*
, не является допустимым? TL; DR, потому что COM не предоставляет гарантий, которые сделали бы это действительным.
Требования COM ошеломительно минималистичны. На самом деле,
Единственное языковое требование для СОМ заключается в том, что код создается на языке, который может создавать структуры указателей и явно или неявно вызывать функции через указатели.
Это позволяет использовать широкий спектр языков программирования для реализации COM-интерфейсов. Обратной стороной этого является то, что COM предлагает очень мало гарантий в отношении того, как ABI сопоставляется с языком- уровневые конструкции. Это особенно верно для наследования интерфейса:
Наследование в COM не означает повторное использование кода.
Реализация IDerived
может выбрать повторное использование реализации IBase
или предоставить собственную реализацию. Это также позволяет вызовам интерфейса IBase
вести себя по-разному в зависимости от того, на каком интерфейсе (IDerived
или IBase
) он вызывается. Это гибкий подход, но с подводным камнем, заключающимся в том, что навигация по иерархии интерфейса с помощью приведения указателей не гарантирует работу.
Но это еще не все! COM имеет другое правило это тривиально легко понять, но часто упускают из виду:
С точки зрения COM-клиента подсчет ссылок всегда выполняется для каждого интерфейса. Клиенты никогда не должны предполагать, что объект использует один и тот же счетчик для всех интерфейсов.
Опять же, это дает реализациям большую гибкость, но требует, чтобы клиенты тщательно управляли своими указателями интерфейса. QueryInterface
— это инструмент, используемый реализацией для отслеживания незавершенных ссылок на интерфейс. Приведение указателей обходит эту важнейшую задачу управления, создавая возможность получить указатель интерфейса, счетчик ссылок которого равен нулю.
Таковы правила и производные гарантии в игре. Теперь в действительности приведения указателей на удивление часто работают. Так что, если вы разработчик, который не слишком различает код, который является правильным, и код, который еще не дал сбоев, тогда во что бы то ни стало двигайтесь вперед и бросайте указатели к удовольствию вашего сердца.
Если, с другой стороны, вы являетесь разработчиком, который гордится тем, что предоставляет программное обеспечение, которое работает благодаря своей правильности, то QueryInterface
всегда требуется для навигации по поверхности интерфейса реализации.
Хорошо, но DirectX на самом деле не использует COM!
Истинный. DirectX использует небольшое подмножество COM, часто называемое Nano-COM< /а>. В то время как большая часть COM не применяется, аспекты ABI COM применимы. Поскольку в этом ответе говорится только об аспектах ABI, он применим как к COM, так и к DirectX.
См. Документы Microsoft.
person
IInspectable
schedule
01.07.2021
cl = gcl
не нужно.gcl
ужеID3D12CommandList*
- person RbMm   schedule 01.07.2021gcl
внутриID3D12CommandList []
(массив), поэтому мне нужно преобразовать вcl
. - person marked-off-topic   schedule 01.07.2021Queryinterface()
, чтобы упростить задачу и не беспокоиться о деталях. - person Remy Lebeau   schedule 01.07.2021QueryInterface
можно использовать и для доступа к родителям? (Потому что я когда-либо использовал его только для доступа к детям) - person marked-off-topic   schedule 01.07.2021Queryinterface()
можно использовать для доступа к любому интерфейсу, который реализует базовый объект. - person Remy Lebeau   schedule 01.07.2021QueryInterface
следует понимать буквально: он спрашивает объект за указателем интерфейса, реализует ли он какой-либо данный интерфейс. Этот интерфейс может быть вверх или вниз по цепочке наследования или вообще не быть частью наследования. Типичным примером являются двойные интерфейсы, гдеIDispatch
обычно реализован как аналог интерфейса(ов) на основе VTable. Так что да, использованиеQueryInterface
- правильный путь. - person IInspectable   schedule 01.07.2021a[i] =(ID3D12CommandList*)gcl
. не нужно вызыватьQueryInterface
, потому что у вас уже есть указатель наID3D12CommandList*
- person RbMm   schedule 01.07.2021ID3D12GraphicsCommandList
унаследовал отID3D12CommandList
- person RbMm   schedule 01.07.2021ID3D12GraphicsCommandList
, содержащая виртуальную таблицуID3D12CommandList
в начале, и любые методыID3D12CommandList
могут быть вызваны этимvtable
. - person RbMm   schedule 01.07.2021I2
(унаследованный отI1
) с указателемp
- мы можем вызвать любой метод изI1
с тем же указателем p - просто потому, что любой метод изI1
- будет методI2
.. - person RbMm   schedule 01.07.2021