Newsgroup: it.comp.lang.c++
Titolo: Interfacce in C++

Salve a tutti
In C++ non esiste il costrutto interface come in Java, ma ho letto che
è possibile "simularlo" attraverso l'eredità virtuale di una classe
astratta pura. Mi spiego meglio con un esempio:
// la mia interfaccia
class Interface
{
public:
   virtuual void methodA() = 0;
   virtual int methodB(int x, int y) = 0;
};
// la mia classe Derived estende una classe base ed implementa un
interfaccia
class Derived : public Base, public virtual Interface
{ ... };
Mi chiedo:
- è questo il modo (più) giusto per raggiungere l'obiettivo
prefissatomi?
- dal punto di vista sintattico, ho qualche implicazione?
- quali sono le implicazioni in termini di performance e di spazio
occupato?
Spero di essere stato abbastanza chiaro. Grazie anticipatamente.

 occupato?
Dipende dell'implementazione, ma in generale, costa meno che un
interface di Java.
--
James Kanze (GABI Software)             
email:james...@gmail.com
Conseils en informatique orientée objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, 
+33 (0)1 30 23 00 34

 occupato?
Teoricamente esattamente le stesse del fare le stesse cose in Java.
 
Ciao,
Nicola

 
Sto studiando ora il C++ ma se vieni da Java non ti puoi preoccupare di 
performances, in quanto non credo tu riuscirai a trovare un linguaggio 
che possa fare peggio di Java! Basta guardare semplicemente come 
gestisce i templates....
Saluti, Alberto

 occupato?
Se il confronto e' java, non mi preoccuperei delle performance ;)
Comunque, la chiamata ad una funzione virtuale e', in genere, piu' lenta 
dell'equivalente non virtuale (ma di _POCO_), con la differenza che un 
ottimizzatore non sempre puo' essere in grado di ottimizzarla mettendola 
inline (diciamo "quasi mai").
--
*PaN!* 


Sicuramente gli "usi e costumi" del C++ sono differenti da quelli di
Java (il fatto che abbia dimenticato il distruttore virtuale è la
prova di abitudini legati all'utilizzo del secondo) e che bisogna
esserne a conoscenza prima di utilizzare il linguaggio.
Nonostante questo il concetto di interfaccia (mediante il quale ci si
assicura che un determinato set di metodi sia presente in un oggetto)
è molto comodo e gradito. Mi sembra invece che in C++ l'eredità
multipla porta ad una duplicazione dei metodi aventi la stessa
signature presenti in sottoclassi diverse, con la necessità di dover
disambiguare. Cioè
se ho
class Base
{
public:
   virtual void methodA() { ... }
   virtual void ~Base() { }
};
class Interface
{
public:
   virtual void methodA() = 0;
   virtual void ~Interface() { }
};
class Derived : public Base, public virtual Interface
{ ... };
Derived* d;
...
d->methodA();
ottengo un errore? Oppure devo disambiguare con i risolutori?

 ottengo un errore? Oppure devo disambiguare con i risolutori?
Nell'esempio, vorrei che venisse invocata l'implementazione di
Base::methodA()
Spero di essere stato chiaro.

 d->methodA();
In realta' cosi' come e' scritto il tuo esempio non puo' generare
ambiguita' perche' Derived deve implementare methodA ed e' quella la
versione che sara' chiamata.
Se pero' lo riscrivi come
#include <iostream>
#include <ostream>
struct B1 {
  virtual int f() { return 42; }
};
struct B2 {
  virtual int f() { return 43; }
};
struct D : public B1,  public B2 {
};
int main() {
  D d;
  std::cout << d.B2::f() << '\n';
}
devi effettivamente disambiguare.
Ciao,
Nicola

 gestisce i templates....
Vorrai dire come *non* gestisce (in modo serio) i templates/generics. :P
Ad ogni modo quello non da problemi di velocità. 
È solo un vomitino venuto male.
-- 
blog:  
http://www.akropolix.net/rik0/blogs
 | Uccidete i filosofi,
site:  
http://www.akropolix.net/rik0/
      | tenetevi riso e
forum: 
http://www.akropolix.net/forum/
     | bacchette per voi.

 del C++.
Infatti, non ha detto l'"obiettivo prefissato". Usare
un'interfaccia comme in Java non è proprio un buon obiettivo,
neanche usare un'interfaccia per usarla. Ma il concetto di
interfaccia serva molta, anche in C++, e fà ben partite degli
"usi e costumi" di C++. (Partite solo, certo.)


 ottengo un errore? Oppure devo disambiguare con i risolutori?
In questo punto, il C++ è più rigoroso (e più robusto) di Java.
Parte dal principio che se Base non conosce il contratto
dall'interfaccia Interface, è poco probabile che l'implementa
correttamente, anche se per caso i nomi delle funzionni son
identici. E che se Base lo conosce, questo contratto, può dirlo,
eritando di Interface. Allora, in C++:
    class Interface
    {
    public:
        virtual ~Interface() { }
        virtual void methodA() = 0;
    } ;
    class Base : public virtual Interface
    {
    public:
        virtual void methodA() ;
    } ;
    class Derived : public virtual Interface, private Base
    {
    } ;
E cosi, puoi scrivere:
    Interface* p = new Derived ;
    p->methodA() ;
Due cose importante qui: l'eritaggio virtual, che fa Java
implicitamente, se erita di una interface, e l'eritaggio privato
di Base, che non è obligato, ma che si fà in generale se Base
non è qu'un detaglia dell'implementazione.



questo va bene se devo scrivere da zero io le tre classi, ma come mi
muovo se già ho Base ed Interface, con le caratteristiche che ho
esposto prima, e devo creare una Derived?

 esposto prima, e devo creare una Derived?
a) Implementi il metodo virtuale di Interface e gli fai chiamare 
esplicitamente il metodo della classe Base
b) Mandi degli sgherri a gambizzare quello che ha scritto Base, che forse 
avrebbe dovuto implementare Interface o ripensare la struttura in altro modo 
=)
--*PaN!*


E perchè? Chi ha scritto Base potrebbe non essere a conoscenza di
Interface.

 Interface.
E proprio per questo e' quanto meno discutibile che Base possa
implementare Interface "per caso".
Ciao,
Nicola

Quindi (e scusate se insisto) se ho
class Base
{
   public:
      virtual int value() { return 100; }
      virtual ~Base() { }
};
class Interface
{
   public:
      virtual int value() = 0;
      virtual ~Interface() = 0;
}
class DerivedA : public Base, public virtual Interface
{
   public:
      virtual ~DerivedA() { }
}
DerivedA non è istanziabile perchè deve implementare
Interface::value() ?
Mentre se scrivo
class DerivedB : public Base, public virtual Interface
{
   public:
      virtual int value() { return Base::value(); }
      virtual ~DerivedB() { }
}
tutto ok, ed ottengo lo stesso comportamento di Java?
Se si, non mi sembra "efficiente" in quanto introduco un ulteriore
chiamata, non credete? Non c'è un modo alternativo più efficiente?
Quali sono i metodi che posso chiamare da un istanza di DerivedB?
Scusate ancora se insisto anche sulle cose più banali, ma ho iniziato
con Java la OO e vorrei imparare il C++. Grazie mille per l'attenzione
e la pazienza.


 chiamata, non credete? Non c'č un modo alternativo piů efficiente?
Lascia perdere l'efficienza. Cerca piuttosto di rendere la gerarchia di 
classi la piů naturale e limpida possibile.
Piů avanti penserai all'efficienza.
Kiuhnm

 > E cosi, puoi scrivere:


La prema domanda allora è, cosa ti fa pensare che Base::methodA
implementa il contrato di Interface. Se l'autore di Base
conosceve il contrato di Interface, e voleva implementarlo,
n'avrebbe derivato. Se non l'ha fatto, è poco probabile che
sarebbe conformo al contratto per caso, non?
Se veramente sei caduto sul caso eccezionnale, e sia 100% sicuro
que Base::methodA convienne, puoi prenderne la risponsibilità,
scrivendo una funzione di Derived:
    virtual void
    Derived::methodA()
    {
        Base::methodA() ;
    }
Ma in questo caso, sei tu chi n'è risponabile, e non il autore
die Base. (Nella pratica, non ho mai visto che va cosi. Si vuol
quasi sempre un po de trattamento di più in Derived, alfine
d'adattare la funzione di Base al contratto di Interface.)


 Quali sono i metodi che posso chiamare da un istanza di DerivedB?
L'alternativa te l'ha mostrata James: basta scrivere
class Base : public virtual Interface
{
   public:
      virtual int value() { return 100; }
      virtual ~Base() { }
};
Il C++ mi pare piu' coerente; vuoi un linguaggio typesafe o no? Se si,
i casi sono due: o Base implementa Interface e bisogna dichiararlo,
oppure non la implementa e bisogna delegare esplicitamente. Altrimenti
perche' richiedere l'uso delle interfacce? Tanto vale affidarsi
completamente al cosiddetto "duck typing".
Ciao,
Nicola

 iniziato con Java la OO e vorrei imparare il C++.
È un probleme, perche Java rende la OO rigorosa molta difficile.


 naturale e limpida possibile.
Cosa chi è essentiale se vuoi essere efficente.


 Mai. L'efficienza è tutto.
All'efficienza penso in seguito. Se il programma è ben strutturato, c'è 
largo spazio per l'ottimizzazione.
Kiuhnm

 largo spazio per l'ottimizzazione.
Uno dei punti del sistema di sviluppo IID (iterative and incremental 
development), il quale ha dato buoni risultati in questi ultimi 50 anni, 
dice:
"Do not optimize during the development process. That is, do not make 
the design more complex just to increase performance. Use simple 
algorithms with acceptable complexity and keep a simple design with the 
right abstractions. Do not worry about the overhead of these 
abstractions. Performance optimization can be done near the end of 
development, but only if there are performance problems. Profiling
should then be used to find those parts of the system (usually very 
small) that should be rewritten."
Kiuhnm

 dice:
+1
Oltrechè anche un qualunque extreme programmer non potrebbe che quotare:
fai le cose fatte bene, ben strutturate. Poi se non sono veloci
abbastanza, velocizza.
Altrimenti si rischia solo di fare un sistema inutilmente veloce: dove
l'inutilmente significa che per farlo veloce si è persa manutensibilità
richiesta (e quindi utile).
Poi chiaro, ci sono campi in cui la velocità è tutto (e magari il
programma ha una struttura relativamente facile).


 l'ottimizzazione.
L'ottimizzazione serva solo per risolvere problemi di
performance. Non sono frequenti: molti programmi son abbastanza
veloce senza ottimizzazione. È propio non è efficiente pensare
alla performance se non c'è bisogna.





Ci son eccezioni: se so che ci son millioni di date, non mi
servo di un'algorithmo O(n^2), neanche nel svilupemente. Ma in
generale: più l'encapsulation (scussi, ma non conosco la parole
italiana) è bene, più è facile ottimizzare quando bisogna.


 servo di un'algorithmo O(n^2), neanche nel svilupemente.
"Use simple algorithms with *acceptable* complexity [...]"
Solo tu puoi decidere cosa è accettabile e cosa non lo è. Quella 
"regola" non cerca di sostituirsi al tuo buon senso.
Kiuhnm