Newsgroup: it.comp.lang.c++
Titolo: omettere keyword virtual in classi derivate

Mi sono spesso chiesto se ci fossero dei vantaggi di qualche tipo nel
consentire al programmatore di non ripetere la keyword virtual nella
dichiarazione degli override. Mi è sempre parso che questa libertà creasse
solo confusione senza aggiungere alcuna comodità, salvo quella di
risparmiare alcune battute.
Ecco, magari ho scoperto l'acqua calda, ma oggi ho trovato un caso in cui
questa libertà torna comoda.
Infatti, se sto definendo una classe template nella quale la classe base è
uno dei parametri, la possibilità di omettere la keyword virtual mi
consente implicitamente di stabilire in fase di instanziazione se
determinati metodi devono essere virtuali o meno, senza alterare la
dichiarazione della classe template.
Potrei, ad esempio, aver bisogno che la medesima implementazione esista
sia sotto forma di classe indipendente, priva di relazioni di derivazione
(nel qual caso il polimorfismo è inutile), sia come espressione concreta
di una certa interfaccia (nel qual caso il polimorfismo è necessario). In
sostanza, intendo qualcosa di questo tipo:
 
  class Interface
  { public:  
    virtual void action() = 0; 
    ~Interface() {}
   };
  class NullInterface { };
  template< class BaseT >
  class MyClass: public BaseT
  {  public:
     void action() { };
  };
A questo punto, MyClass<Interface>::action sarà virtual, mentre
MyClass<NullInterface>::action no.
Beh, come ho detto, probabilmente ho scoperto l'acqua calda.
Max 

    };
Questa accopiata di funzione virtuale pura con un construttore non virtuale
mi lascia perplesso......
----
Gaetano.
-- 
Posted from [194.214.173.28] 
via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 virtuale mi lascia perplesso......
Intendevi "distruttore", ovviamente. :) 
E' una dimenticanza, in effetti, l'assenza non era voluta.
Max

 E' una dimenticanza, in effetti, l'assenza non era voluta.
:-)
Comunque quello che dici mi sembra giusto va fatto un attimo un po di
chiarezza
perche' non si envice dall'esempio quello che volevi dire:
Credo che tu volessi dire questo:
class BV    // Base con action virtuale
{
  public:
   virtual void action() { std::cout << "BV" << std::endl; }
      virtual ~BV() {}
};
class BN    //Base con action Non virtuale
{
  public:
    void action() { std::cout << "BN" << std::endl; }
    virtual ~BN() {}
};
template <class BaseT>
class D: public BaseT
{
   public:
    void action() { std::cout << "D" << std::endl; }
    virtual ~D() {}
};
template <class T>
class MyClass : public D<T>
{
   public:
    void action() { std::cout << "MyClass" << std::endl; }
};
a questo punto inserendosi' a meta' gerarchia:
D<BN>* p = new MyClass<BN>;
 p->action();  // ottengo la chiamata di D::action
mentre con
D<BV>* p = new MyClass<BV>;
 p->action();  // ottengo la chiamata di MyClass::action
Ganzo. (Alexandrescu ci tirerebbe sicuramente fuori qualche
cosa).


In generale e' meglio evitare di ridefinire una funzione membro non
definita come virtuale in una superclasse, perche' questo costituisce
una violazione del contratto della superclasse.
Per questo preferisco l'esempio di Max, nel quale la funzione membro in
questione non e' proprio definita. Mi sa che la difficolta' di venir
fuori con un esempio piu' concreto e' un indicazione dell'effettiva
utilita' di questa tecnica.
In realta' credo che l'omissibilita' della parola chiave virtual nelle
classi derivate e' solo un aiuto all'eventuale ridisegno: l'aggiunta a
posteriori di una classe base provoca l'automatica "virtualizzazione"
delle opportune funzioni membro, evitando errori subdoli.
Ciao,
Nicola


Le mie cosiderazioni, comunque, nascevano proprio da un preciso problema
concreto con cui mi ero appena imbattuto.
La situazione era questa.
Ho un certo numero di classi concrete, prive di qualsivoglia relazione di
parentela tra loro o con classi base, ma dotate di una comuna, chiamamola,
"interfaccia compile-time", nel senso che sono state concepite per essere
impiegate come parametri per un certo insieme di classi e funzioni
template.
Ora, in un contesto diverso da quello iniziale, mi accorgo che la
sostituibilità compile-time non mi basta più, perchè ho l'esigenza di
poter selezionare un'implementazione appropriata in fase di esecuzione. Mi
servivano perciò delle classi che fossero perfettamente analoghe a quelle
che avevo, ma che garantissero il polimorfismo.
A questo punto, avevo tre alternative:
1. derivare comunque da un'interfaccia e rendere quindi virtuali i metodi
interessati, indipendentemente dall'uso che avrei fatto delle classi 
2. creare classi wrapper derivate da un'interfaccia e usare contenimento e
delegazione 
3. usare la tecnica che ho descritto nel mio messaggio
La 1. crea un'inutile overehead, quando le classi devono essere impiegate
nel primo contesto. La 2. crea delle specie di doppioni, con un certo
possibile overhead se non posso contare sull'inlining dei metodi
dell'oggetto contenuto. La 3 è perfetta: ho un'unica implementazione, non
ho l'overhead del polimorfismo, se non quando mi serve. Se lo standard non
consentisse di omettere la keyword virtual immagino che quest'ultima non
sarebbe un'alternativa possibile.
Max