Если вы ищете полностью общий способ манипулирования объектами во время выполнения, когда вы не знаете их типы во время компиляции в C ++, вам, по сути, необходимо:
- Определите интерфейс (абстрактный базовый класс со всеми чисто виртуальными методами и без членов) для каждой возможности, которую может поддерживать класс.
- Каждый класс должен наследовать практически от всех интерфейсов, которые он хочет реализовать (возможно, среди других классов).
Теперь предположим, что pFoo
содержит указатель интерфейса типа IFoo*
на некоторый объект x
(вам не нужно знать конкретный тип x
). Вы можете увидеть, поддерживает ли этот объект интерфейс IBar
, сказав:
if (IBar* pBar = dynamic_cast<IBar*>(pFoo)) {
// Do stuff using pBar here
pBar->endWorldHunger();
} else {
// Object doesn't support the interface: degrade gracefully
pFoo->grinStupidly();
}
Этот подход предполагает, что вы знаете все соответствующие интерфейсы во время компиляции - если вы этого не сделаете, вы все равно не сможете использовать обычный синтаксис C ++ для вызова методов. Но трудно представить себе ситуацию, когда вызывающая программа не знает, какие интерфейсы ей нужны - о единственном случае, о котором я могу подумать, это о том, если вы хотите представить объекты C ++ через интерактивный интерпретатор. Даже в этом случае вы можете придумать (уродливый, требующий больших затрат на обслуживание) способ вставить это в вышеприведенную парадигму, чтобы методы можно было вызывать, указав их имена и аргументы в виде строк.
Другим аспектом, который следует учитывать, является создание объекта . Чтобы выполнить это, не зная конкретных типов, вам понадобится функция фабрики плюс уникальные идентификаторы для классов, чтобы указать, какой конкретный класс вы хотите. Можно организовать, чтобы классы регистрировались в глобальной фабрике при запуске , как описано здесь экспертом по C ++ Хербом Саттером - это позволяет избежать поддержки гигантского оператора switch
, что значительно упрощает обслуживание. Можно использовать одну фабрику, хотя это подразумевает, что существует один интерфейс, который должен реализовывать каждый объект в вашей системе (фабрика будет возвращать указатель или ссылку на этот тип интерфейса).
В конце концов, то, что вы получите, в основном (изоморфно) COM - dynamic_cast<IFoo*>
выполняет ту же работу, что и QueryInterface(IID_IFoo)
, а базовый интерфейс реализован всеми объектами эквивалентно IUnknown
.