Penso di comprendere le attuali limitazioni del polimorfismo in fase di compilazione e del polimorfismo in fase di esecuzione. Ma quali sono le differenze concettuali tra interfacce esplicite (polimorfismo run-time. Cioè funzioni virtuali e puntatori / riferimenti) e interfacce implicite (polimorfismo tempo di compilazione. Cioè modelli) .
I miei pensieri sono che due oggetti che offrono la stessa interfaccia esplicita devono essere lo stesso tipo di oggetto (o hanno un antenato comune), mentre due oggetti che offrono la stessa interfaccia implicita non devono necessariamente essere lo stesso tipo di oggetto e, escludendo l'implicito l'interfaccia che entrambi offrono, può avere funzionalità abbastanza diverse.
Qualche idea su questo?
E se due oggetti offrono la stessa interfaccia implicita, quali sono i motivi (oltre al vantaggio tecnico di non aver bisogno di un dispacciamento dinamico con una tabella di ricerca delle funzioni virtuali, ecc.) Per non avere questi oggetti ereditati da un oggetto base che dichiara tale interfaccia, quindi rendendolo un'interfaccia esplicita ? Un altro modo di dirlo: puoi darmi un caso in cui due oggetti che offrono la stessa interfaccia implicita (e quindi possono essere usati come tipi per la classe modello di esempio) non dovrebbero ereditare da una classe base che rende esplicita tale interfaccia?
Alcuni post correlati:
- https://stackoverflow.com/a/7264550/635125
- https://stackoverflow.com/a/7264689/635125
- https://stackoverflow.com/a/8009872/635125
Ecco un esempio per rendere più concreta questa domanda:
Interfaccia implicita:
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();
}
};
Interfaccia esplicita:
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();
}
};
Un esempio ancora più approfondito e concreto:
Alcuni problemi di C ++ possono essere risolti con:
- una classe modello il cui tipo di modello fornisce un'interfaccia implicita
- una classe non basata su modelli che accetta un puntatore di classe base che fornisce un'interfaccia esplicita
Codice che non cambia:
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 . Una classe non basata su modelli che accetta un puntatore di classe base che fornisce un'interfaccia esplicita:
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 . Una classe basata su modelli il cui tipo di modello fornisce un'interfaccia implicita:
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 . Una classe basata su modelli il cui tipo di modello fornisce un'interfaccia implicita (questa volta, non derivante da 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;
}
Il caso 1 richiede che l'oggetto che viene passato useCoolClass()
sia figlio di CoolClass
(e attrezzo worthless()
). I casi 2 e 3, d'altra parte, prenderanno qualsiasi classe che ha una doSomethingCool()
funzione.
Se gli utenti del codice sono sempre stati bene sottoclasse CoolClass
, allora il caso 1 ha un senso intuitivo, poiché si CoolClassUser
aspetterebbe sempre un'implementazione di a CoolClass
. Ma supponiamo che questo codice faccia parte di un framework API, quindi non posso prevedere se gli utenti vorranno sottoclassare CoolClass
o eseguire il roll della propria classe che ha una doSomethingCool()
funzione.