Acho que compreendo as limitações reais do polimorfismo em tempo de compilação e do polimorfismo em tempo de execução. Mas quais são as diferenças conceituais entre interfaces explícitas (polimorfismo em tempo de execução. Isto é, funções virtuais e ponteiros / referências) e interfaces implícitas (polimorfismo em tempo de compilação. Ie. Modelos) .
Penso que dois objetos que oferecem a mesma interface explícita devem ter o mesmo tipo de objeto (ou ter um ancestral comum), enquanto dois objetos que oferecem a mesma interface implícita não precisam ter o mesmo tipo de objeto e, excluindo o implícito interface que ambos oferecem, pode ter uma funcionalidade bastante diferente.
Alguma idéia sobre isso?
E se dois objetos oferecem a mesma interface implícita, por que razões (além do benefício técnico de não precisar de expedição dinâmica com uma tabela de pesquisa de funções virtuais etc.), existem para não ter esses objetos herdados de um objeto base que declara essa interface, portanto tornando-o uma interface explícita ? Outra maneira de dizer: você pode me dar um caso em que dois objetos que oferecem a mesma interface implícita (e, portanto, podem ser usados como tipos para a classe de modelo de exemplo) não devem herdar de uma classe base que torna explícita a interface?
Algumas postagens relacionadas:
- https://stackoverflow.com/a/7264550/635125
- https://stackoverflow.com/a/7264689/635125
- https://stackoverflow.com/a/8009872/635125
Aqui está um exemplo para tornar essa pergunta mais concreta:
Interface implícita:
class Class1
{
public:
void interfaceFunc();
void otherFunc1();
};
class Class2
{
public:
void interfaceFunc();
void otherFunc2();
};
template <typename T>
class UseClass
{
public:
void run(T & obj)
{
obj.interfaceFunc();
}
};
Interface explícita:
class InterfaceClass
{
public:
virtual void interfaceFunc() = 0;
};
class Class1 : public InterfaceClass
{
public:
virtual void interfaceFunc();
void otherFunc1();
};
class Class2 : public InterfaceClass
{
public:
virtual void interfaceFunc();
void otherFunc2();
};
class UseClass
{
public:
void run(InterfaceClass & obj)
{
obj.interfaceFunc();
}
};
Um exemplo concreto ainda mais profundo:
Alguns problemas de C ++ podem ser resolvidos com:
- uma classe de modelo cujo tipo de modelo fornece uma interface implícita
- uma classe não modelo que usa um ponteiro de classe base que fornece uma interface explícita
Código que não muda:
class CoolClass
{
public:
virtual void doSomethingCool() = 0;
virtual void worthless() = 0;
};
class CoolA : public CoolClass
{
public:
virtual void doSomethingCool()
{ /* Do cool stuff that an A would do */ }
virtual void worthless()
{ /* Worthless, but must be implemented */ }
};
class CoolB : public CoolClass
{
public:
virtual void doSomethingCool()
{ /* Do cool stuff that a B would do */ }
virtual void worthless()
{ /* Worthless, but must be implemented */ }
};
Caso 1 . Uma classe sem modelo que usa um ponteiro de classe base que fornece uma interface explícita:
class CoolClassUser
{
public:
void useCoolClass(CoolClass * coolClass)
{ coolClass.doSomethingCool(); }
};
int main()
{
CoolA * c1 = new CoolClass;
CoolB * c2 = new CoolClass;
CoolClassUser user;
user.useCoolClass(c1);
user.useCoolClass(c2);
return 0;
}
Caso 2 . Uma classe de modelo cujo tipo de modelo fornece uma interface implícita:
template <typename T>
class CoolClassUser
{
public:
void useCoolClass(T * coolClass)
{ coolClass->doSomethingCool(); }
};
int main()
{
CoolA * c1 = new CoolClass;
CoolB * c2 = new CoolClass;
CoolClassUser<CoolClass> user;
user.useCoolClass(c1);
user.useCoolClass(c2);
return 0;
}
Caso 3 . Uma classe de modelo cujo tipo de modelo fornece uma interface implícita (desta vez, não derivada de CoolClass
:
class RandomClass
{
public:
void doSomethingCool()
{ /* Do cool stuff that a RandomClass would do */ }
// I don't have to implement worthless()! Na na na na na!
}
template <typename T>
class CoolClassUser
{
public:
void useCoolClass(T * coolClass)
{ coolClass->doSomethingCool(); }
};
int main()
{
RandomClass * c1 = new RandomClass;
RandomClass * c2 = new RandomClass;
CoolClassUser<RandomClass> user;
user.useCoolClass(c1);
user.useCoolClass(c2);
return 0;
}
O caso 1 exige que o objeto que está sendo passado useCoolClass()
seja filho de CoolClass
(e implemente worthless()
). Os casos 2 e 3, por outro lado, terão qualquer classe que tenha uma doSomethingCool()
função.
Se os usuários do código sempre subclassificassem bem CoolClass
, o Caso 1 CoolClassUser
faria sentido intuitivo, pois sempre estaria esperando uma implementação de a CoolClass
. Mas suponha que esse código faça parte de uma estrutura de API, portanto, não posso prever se os usuários desejarão subclassificar CoolClass
ou rolar sua própria classe que possui uma doSomethingCool()
função.