Primero, algunos antecedentes técnicos: los compiladores de C++ generalmente generan algo llamado "vtable" para cualquier clase con funciones virtuales. Esto es básicamente una tabla de indicadores de función. El vtable contiene un puntero de función para cada método virtual implementado por una clase.
En COM, las interfaces son básicamente las clases base abstractas que unos implementos de componentes, por ejemplo .:
class CSomeComponent : IUnknown, ISomeOtherInterface { ... };
la viable para CSomeComponent
incluirán los punteros de función para todos los métodos definidos en estas dos interfaces.
struct __imaginary_vtable_for_CSomeComponent
{
// methods required by IUnknown
HRESULT (*QueryInterface)(const IID& iid, void** ppv);
ULONG (*AddRef)();
ULONG (*Release)();
// methods required by ISomeOtherInterface
void (*foo)();
...
};
Cualquier objeto instanciado tiene una referencia al vtable de su tipo dinámico. Así es como el programa sabe cómo llamar al método apropiado en los casos en que se anula un método de base en una clase derivada:
class Base
{
public:
virtual void foo() { ... }
}
class Derived : public Base
{
public:
virtual void foo() { ... } // overrides Base::foo()
virtual void bar() { ... }
}
...
Base* X = new Derived;
X->foo();
La última línea debe llamar Derived::foo
. Esto funciona porque el objeto X
tiene una referencia al vtable para la clase Derived
. Como se dijo, el vtable es como una lista de indicadores de función. Ahora, vtables tienen un diseño fijo: Si la clase Derived
hereda de la clase Base
, el puntero de función para el método foo
estarán en la misma posición relativa en Derived
's vtable que en Base
' vtable s:
struct __imaginary_vtable_for_Base
{
void (*foo)();
};
// __imaginary_vtable_for_Base::foo = Base::foo
struct __imaginary_vtable_for_Derived
{
void (*foo)();
void (*bar)();
};
// __imaginary_vtable_for_Derived::foo = Derived::foo
ahora, si el compilador ve algo como X->foo()
, sabe que para todas las clases derivadas de Base
, el método foo
corresponde a la primera entrada en el vtable. Por lo tanto, emite una llamada al primer puntero a función, que en el caso X
es una llamada al Derived::foo
.
Respuesta a su pregunta: Los compiladores solo pueden generar componentes COM si generan la misma disposición para los vtables que la especificación COM. Los vtables se pueden implementar de varias maneras diferentes, especialmente cuando se trata de herencia múltiple (que se requiere con los componentes COM). Es necesario seguir cierto formato vtable para que cuando llame al método f
, llame al método f
y no a ningún otro método g
que se encuentre en la posición f
en el vtable de la clase de componente. Supongo que los compiladores COM-compliant esencialmente tienen que producir los mismos diseños de vtable que Microsoft Visual C++, ya que la tecnología COM fue definida por Microsoft.
P.S.: Disculpe por ser tan técnico, espero que la información anterior sea de alguna utilidad para usted.
Esta publicación de blog de MSDN (http://blogs.msdn.com/oldnewthing/archive/2004/02/05/68017.aspx) también podría ayudar. – stakx
+1 para una explicación muy agradable. – Ashish