Newsgroup: it.comp.lang.c++
Titolo: C++ e C

Ho bisogno di un chiarimento di fondo.
Iniziai circa 20 anni fa con l'assembler, del quale resto sempre innamorato.
Mi fu facile passare al C, dato che come linguaggio e' giusto un livello
sopra l'assembler.
Poi abbandonai la programmazione per vari motivi.
Ora che l'ho ripresa, mi trovo davanti il C++ e le...classi con qualche
difficolta' di comprensione di fondo.
Brevemente per non annoiare:
a cosa serve, in pratica, tutta l'impalcatura delle classi?
Premetto che ho gia' letto a sufficienza ma che non riesco ad entrare
'concettualmente' nello spirito...di classe.
Se OT potete anche rispondermi privatamente
In ogni caso, grazie
Enzo Franchini

'concettualmente' nello spirito...di classe.
L'impalcatura delle classi e' di supporto per chi vuole programmare
in C++ utilizzando un approccio "Object Oriented" (chi non utilizza
approcci object oriented a volte usa comunque il C++ per altri
vantaggi che offre, come exception e template).
La programmazione object oriented e' fondata su due concetti
principali: incapsulazione e polimorfismo.
Con incapsulazione si intende dividere la soluzione del problema
in entita' separate in cui ogni entita' sia responsabile del
suo sottoproblema, con i suoi dati e con i suoi algoritmi.
La differenza rispetto alla programmazione a "moduli" comune
in C e' che di una entita' possono convivere nello stesso
programma piu' istanze indipendenti, ciascuna con i propri dati.
Ad esempio in un programma grafico una possibile entita' sarebbe
la finestra, che gestisce i suoi dati e i suoi algoritmi e in
cui gli "utilizzatori" che accedono tramite una interfaccia
dichiarata esplicitamente. Un parallelo nel mondo dell'elettronica
potrebe essere un circuito integrato; ma diversamente dal mondo
dell'elettronica pero' risulta possibile creare nuove istanze di
circuito integrato durante l'esecuzione estendendo il circuito.
Con polimorfismo (chiamato anche late-binding) si indica
l'utilizzare diversi oggetti incapsulati che pero' hanno una
interfaccia comune senza sapere quale e' l'oggetto esatto a
cui ci si sta interfacciando. Un parallelo nel mondo
dell'elettronica sarebbe il poter cambiare i collegamenti
durante l'esecuzione sostituendo ad esempio una periferica che
riceve un input con un'altra compatibile a livello di segnale
senza che la sorgente sia interessata alla sostituzione.
Il termine "late binding" indica che il codice esatto che
verra' chiamato e' ignoto al momento della compilazione e
quindi la risoluzione degli indirizzi viene effettuata al
momento dell'esecuzione (in C la cosa si ottiene con chiamate
tramite puntatori a funzione).
L'approccio object oriented offre il grande vantaggio di
alzare il livello dell'analisi. Dopo che si e' acquisita
una buona esperienza di scrittura codice diventa piu'
naturale scrivere correttamente le interfacce e la suddivisione
in moduli e a questo punto diventa molto piu' semplice
progettare software di dimensioni maggiori e piu' sofisticato
con meno errori e con meno sforzo. Il salto e' veramente
notevole, paragonabile a quello permesso da un approccio
strutturato alla programmazione quando si passa dai salti
condizionali a for/while/if.
Dopo aver adottato la prograzione object oriented in Pascal
sono andato avanti per molto tempo a scrivere codice C con
questa filosofia ma oggi che il C++ si e' stabilizzato non
mi sembra piu' un approccio sensato. Il C++ offre molte cose
gia' implementate per chi vuole lavorare object oriented e
l'implementazione e' sicuramente migliore di quella che un
programmatore si potrebbe permettere di realizzare in C.
Io non sono un tifoso di ogni singolo aspetto del C++, ma
il C++ e' un linguaggio multiparadigma che puo' avere
diversi impieghi e che puo' anche essere appreso e
utilizzato un poco alla volta. Sono anche convinto che si
tratti di qualcosa che e' arrivato per restare per lungo
tempo (come C o COBOL) e quindi merita sicuramente
l'attenzione di un informatico.
Io ho imparato quello che so del C++ a spizzichi e bocconi
e solo recentemente mi sono messo a leggere sistematicamente
qualche libro. "The C++ programming language" (terza edizione)
puo' non essere il massimo per un neofita ma per persone che
comprendono il bene il C mi sembra un buon libro. Una volta
appresa la sintassi ci si puo' rivolgere a libri che si
spingono oltre e offrono una visione di cio' che e' meglio
fare o evitare, come "C++ FAQs" di Marshall Cline oppure
"Effective C++/More Effective C++" di Scott Meyers.
Si tratta di qualcosa di decisamente piu' vasto del C e
quindi l'importante e' non scoraggiarsi al primo impatto.
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff

Credo che poche parole possano aiutrati di più di un lunga spiegazione (Per
questo ci sono i libri :)
Ti dico quali erano i miei dubbi prima e durante l'apprendimento del C++ e
le risposte ora:
1) Se devo realizzare degli oggetti "generici" non perdo in efficenza sia
come velocità che impiego di risorse?
R: Si in teoria No in pratica visto che il poco che perdi lo riguadagni
nella semplicità di realizzazione di tutto il progetto che in generale sara'
più compatto e magari veloce.
2) Tutto quello che faccio in C++ potrei comunque realizzarlo in C (Dal
punto di vista funzionale)  dunque perche' dovrei usare il C++?
R: E' vero MA in C++ ogni oggetto una volta realizzato e' finito una volta
per tutte e accedendo all'interfaccia lo uso senza dovermi più preoccupare
di nulla per quanto lo riguarda in C invece la cosa risulta meno sicura
anche utilizzando i moduli.
In definitiva il C++ aiuta di molto lo sviluppo rapido di grossi programmi
senza perdere la potenza del C.
Questa e' la mia opignone
    Ciao
        Carlini Alessio

 qualche libro.
Da quello che leggo nei tuoi interessanti messaggi direi che questo tipo
di apprendimento ha dato ottimi frutti.
EMG

di apprendimento ha dato ottimi frutti.
Hehehe... ringrazio, ma piu' guardo il C++ e piu' mi rendo
conto che si tratta di un mostro immenso. Io cerco di
controllare prima di scrivere sul newsgroup ma qualche volta
la mia intuizione mi ha fatto dire castronerie (in C++ non
sempre l'intuizione e' buona consigliera).
Trust no one ;)



Debbo dire che mi manca una base di così basso livello, comunque
la mia passione mi spinge a leggere tutti i sorgenti su cui riesco
a mettere le mani.
In questo modo mi sono fatto una idea di come funzionano le cose.
Comunque uno dei meriti del C++ è che ti permette di scrivere buoni
programmi pensando solo al problema che devi risolvere.
Una cosa di cui sento la mancanza sono delle librerie scritte in
C++ e degne di questo nome.
Purtroppo il mondo GNU è legato al C e anche se si vedono in giro
delle cose in C++, si vede subito che chi le ha scritte ha in mente
il C e non il C++.
Spero che le cose cambino in futuro (ma ci spero poco).
Manlio Perillo

più compatto e magari veloce.
A mio parere, No, neanche in teoria, visto che il template viene risolto a
compile-time e diventa una classe come tutte le altre.
Se poi mi si risponderà che una classe generica non può lavorare con un tipo
di dato con la stessa efficienza del C, allora significa che quella classe:
1) Non è _abbastanza_ generica;
2) Va specializzata per il tipo di dato che si intende trattare.
Non a caso per i template è prevista la "specializzazione"
Ciao ;-)
Domenico

Innanzitutto grazie a tutti delle risposte. Mi ha fatto piacere averle
stimolate in professionisti del C++ e C ( si deduce molto bene da quanto
scrivete).
Un buon messaggio per il sottoscritto: e cioe' che posso traghettare dal C
al C++ per gradi. E cio' mi conforta perche', leggendone i manuali, mi son
reso conto che mi trovo di fronte ad una creatura gigantesca con mille teste
ma affascinante ed ammaliante nello stesso tempo.
Guardavo in questi giorni le mie vecchie routine in C misto ad assembler,
deputate a gestire la grafica (image processing) e l'allocazione di
memoria.Inimmaginabile passare dagli Int xxh. Mamma mia, ogni scheda video
aveva i suoi registri e i suoi codici. Ogni CPU nuova aveva nuovi codici,
286,386, coprocessori dedicati...
Scusate ho divagato.
Un ultima domanda e poi vi lascio alla vostra tastiera:
se ho ben capito, potrei trasformare le mie vecchie strutture, quelle che
risultassero ancora utili, in classi?
Grazie ancora a tutti..
Enzo Franchini



 conto che si tratta di un mostro immenso.
E' proprio vero. Non sarà veramente un po' eccessivo?...
EMG

 risultassero ancora utili, in classi?
In C++ strutture e classi sono la stessa cosa (a parte il default per
l'accesso ai membri) e quindi vai tranquillo.
EMG

 Non a caso per i template è prevista la "specializzazione"
Quello che volevo dire non e' che il C++ introduca overhead ma che la
programmazione OO può in alcuni casi portare ad implementare funzioni e/o
rappresentare informazioni non strettamente necessarie ai fini
dell'applicativo per motivi (Nobili) di completezza, aggiornabilità o ...
megalomania ;)
Ad esempio ho appena realizzato un programma di cui un buon 20/30% non
richiesto dalle specifiche maaaa chi può dire che poi non mi serva!!! Meglio
una classe completa subito che una classe inutile domani :)
    Ciao
        Carlini Alessio


Non è detto che tutto si debba risolvere in classi e OOP.
Uno dei vantagi del C++ è che è multi-paradigma.
Se una disegno non beneficiasse della OOP è inutile
forzarlo.
Comunque il C++ offre altri strumenti per gestire i progetti,
come namespace e template.
Se invece ti ritrovi con una struttura che presenta una
certa interfaccia utente ed in cui varia la implementazione
(ad esempio funzioni per la grafica con diverse schede grafiche),
l' OOP aiuta nella gestione del progetto.
Manlio Perillo

a cosa serve, in pratica, tutta l'impalcatura delle classi?
Le classi sono il fondamento della programmazione ad oggetti
in C++. La programmazione ad oggetti a sua volta e` un paradigma
di programmazione per la realizzazione di applicazioni piu` modulari
e manutenibili.
Per maggiori info puoi provare a leggere una breve introduzione alla
OOP che ho realizzato e che trovi sulla mia home page.
Spero possa esserti utile.
Paolo
-------------------------------------------
  Paolo Marotta
 
http://web.tiscalinet.it/paolo_marotta/
-------------------------------------------

object oriented. Ovviamente e' solo la mia opinione.
Su questo concordo... non c'e` solo l'ereditarieta` ed il polimorfismo
puo` essere ottenuto in tanti modi (es. il polimorfismo parametrico
dei template), ma nel contesto del C++ e` comunque un aspetto
di primaria importanza.


si scelga) perderebbe gran parte della sua potenza.
Per la mia esperienza posso dire che l'ereditarieta' (al di fuori
di essere un mezzo per ottenere polimorfismo in un linguaggio
strong typed) non gioca un ruolo chiave. In particolare le
derivazioni in un buon programma a oggetti sono normalmente poche
e poco profonde (ad esempio l'infilare tutto in una gigantesca
gerarchia come qualche framework propone mi sembra piu' una
complicazione che una semplificazione del problema).
La derivazione utilizzata dove non relmente necessario
irrigidisce il design e porta a codice non riutilizzabile e
con classi troppo interdipendenti.
Io ho visto per la prima volta la programmazione object
oriented su un libro (peraltro ben scritto) in cui si faceva
discendere l'entita' cerchio dall'entita' punto ("in fondo un
cerchio e' un punto con un raggio") per poter recuperare il
codice del metodo di traslazione.
Attualmente sono convinto che una scelta simile sia quasi
sempre un errore di design abbastanza grave.
Io credo che il grosso salto rispetto alla decomposizione
moduli e funzioni sia rappresentato dalla incapsulazione e dal
polimorfismo. Entrambe cose che non sono strettamente connesse
con l'ereditarieta'; il polimorfismo e' connesso con
l'ereditarieta' in C++ e in altri linguaggi, ma solo come
conseguenza della tipizzazione rigida. In altri ambienti di
programmazione object-oriented questa associazione rigida fra
polimorfismo ed ereditarieta' non esiste (e' possibile chiamare
un metodo di un oggetto senza conoscere NULLA della sua
interfaccia; nel caso che il metodo non sia accettabile si
ottiene un errore al momento dell'esecuzione).
Non sto dicendo che un sistema sia meglio dell'altro,
i due approcci (come quasi sempre accade) hanno pro e contro;
semplicemente, secondo la mia opinione, i veri cardini della
programmazione object oriented in senso generale non sono
connessi con l'ereditarieta'.
Perche' dici che senza ereditarieta' si perderebbe gran
parte della potenza espressiva della programmazione
object oriented ? Un linguaggio object oriented in cui
il concetto di ereditarieta' non esistesse sarebbe a
tuo parere inutilizzabile o troppo limitato ?


perdere i benefici di riusabilità che la OOP può dare se ben utilizzata.
Non sarei cosi` rigoroso, l'ereditarieta` di implementazione
stabilisce un forte legame tra la classe base e quella ereditata,
tuttavia in alcune situazioni cio` potrebbe non essere un problema
(tipicamente quando aggiungi nuovo comportamento senza alterare
quello ereditato...).
E` comunque vero che una tale scelta va ponderata assai accuratamente!


 una classe completa subito che una classe inutile domani :)
La programmazione ad oggetti non porta da nessuna parte; se sei abituato
a riempire i programmi di cose inutili, nulla ti impedisce di farlo in C
o anche in assembly.
Una classe piena di cose inutili oggi *e'* peggio. Nel migliore dei casi
fatica sprecata, nel peggiore ci vorra' del lavoro in piu' per disfare
le cose inutili e rifarle come servono realmente.
Ciao,
Nicola

 Nicola
Non hai capito nulla ma non fa nulla
    Ciao

di bruciare neuroni... non ascoltarli :)
sicuramente l'assembler puo' servire in applicazioni *realmente*
time-critical ; ma non lo potrai usare mai in codice che deve essere
portabile. molte volte poi l'ottimizzazione a livello algoritmico ti
porta gia' al 90% del guadagno.
friol

friol



Invece io credo cha abbia capito... Tu stai parlando di "completare" le
classi con tutta una serie di metodi che non usi nel tuo applicativo, ma
che potresti usare in un applicativo successivo. Vuoi cioe' scrivere
contemporaneamente una libreria e un applicativo. Non solo. Vuoi
scrivere la tua libreria di oggetti usando la tecnica "fat interface":
oggetti con innumerevoli metodi, "just in case".
Quello che osserva giustamente Nicola e' che si scrivono MOLTI piu'
applicativi che librerie. Pretendere di trasformare le classi create per
un applicativo in libreria significa prima costruire il tetto, poi le
fondamenta.
Una libreria ha caratteristiche di genericita' ed espandibilita'. Prendi
la standard libray. E' stato notato in un thread che non e' disponibile
un modo per inizializzare un std::vector con una array. Questa
inizializzazione non e' considerata "generica". I costruttori di un
vector sono solo 2: il costruttore di copia (con la possibilita' di
copiare solo una parte del vettore espicitamente scritta per aumentare
la prestazioni), il costruttore a partire da un singolo oggetto (con la
possibilita' di costruire un vettore di N oggetti uguali, sempre per
aumentare le prestazioni). E basta. Se un applicativo vuole fare
qualcosa di piu' specifico con i vettori, si scrive una sua classe (che
usera' solo lui!) che incapsula un vettore.
Espandere le classi create per un applicativo con metodi che non vengono
usati "just in case" Nel migliore dei casi e' fatica sprecata, nel
peggiore ci vorra' del lavoro in piu' per disfare le cose inutili e
rifarle come servono realmente. Cut and paste from Nicola :)

 un modo per inizializzare un std::vector con una array.
Per quel che mi risulta, e' perfettamente legale il seguente codice:
#include <vector>
#include <iostream>
int main() {
  int array[] =  { 1, 2, 3, 42, 666 };
  std::vector<int> v(array, array+5);
  std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, "\n"));
}
Lo standard (23.2.4.2) prevede un costruttore che accetta come
argomenti due InputIterators; entita' che dereferenziate diano uno
std::vector::value_type, e che possano sia essere incrementate che
confrontate per l'uguaglianza.
Incidentalmente Bjarne Stroustrup usa lo stesso codice a pagina 448:
  char p[] = "despair";
  vector<char> v2(p, &p[sizeof(p)-1]); // copy characters from C-style string
mentre Stan Lippmann e Josée Lajoie lo fanno a pagina 122:
  int ia[6] = { -2, -1, 0, 1, 2, 1024 };
  // copies the 6 elements of ia into ivec
  vector<int> ivec( ia, ia+6 );
Come la mettiamo?  Ammetto che il thread citato mi era sfuggito, e
posso aver capito male il riferimento...
-- 
Maurizio Loreti                         
http://www.pd.infn.it/~loreti/mlo.html
Univ. of Padova, Dept. of Physics - Padova, Italy            
lor...@pd.infn.it

Mi spiace contraddire Guido che mi ha dato man forte :-) In effetti come
mostra Maurizio l'esistenza del costruttore basato su iteratori e'
sufficientemente generale da includere l'inizializzazione da array:
passare begin ed end e' concettualmente equivalente a passare begin e
numero degli elementi.
Ciao,
Nicola

 --
Hai ragione... il thread citato e' "inizializzazione std::vector"
12/9/2000 
Il riferimento che ho fatto e' incompleto ed era meglio se riportavo il
thread: ma, alla lettera, effettivamente si puo' costruire un vettore
usando i due address (inizio, fine) di una array (ed in effetti e' molto
utile "in generale"). 
Il problema che avevo in mente e' piu' particolare: come gestire la
costruzione di un vettore const in una classe. Ed e' proprio il caso in
cui una libreria non dovrebbe intervenire! ma un applicativo puo' farlo.

 aumentare le prestazioni). E basta.
A me risultano cinque costruttori per il tipo std::vector <>:
explicit vector(const Allocator& alloc = Allocator());
The default constructor. Creates a vector of length zero. The vector
will use the allocator alloc for all storage management.
explicit vector(size_type n);
Creates a vector of length n, containing n copies of the default value
for type T. Requires that T have a default constructor. The vector will
use the allocator Allocator() for all storage management.
vector(size_type n, const T& value,
const Allocator& alloc = Allocator());
Creates a vector of length n, containing n copies of value. The vector
will use the allocator alloc for all storage management.
vector(const vector<T, Allocator>& x);
Creates a copy of x.
template <class InputIterator>
vector(InputIterator first, InputIterator last,
const Allocator& alloc = Allocator());
Creates a vector of length last - first, filled with all values obtained
by dereferencing the InputIterators on the range [first, last). The
vector will use the allocator alloc for all storage management.
e quest'ultimo può essere utilizzato per inizializzarlo con i valori
presi da un array.
EMG

tuo parere inutilizzabile o troppo limitato ?
Se forniss strumenti alternativi, potrebbe essere
la stessa cosa, bisognerebbe valutare con attenzione...
in generale comunque trovo l'ereditarieta` uno strumento
molto potente, spesso mi capitano situazioni in cui ci sono
molte entita` in relazione is-a... l'ereditarieta` e` un mezzo potente
che mi consente il riuso, il polimorfismo mi consente solo
di maneggiare le varie entita` allo stesso modo.
Direi che incapsulazione, ereditarieta` e polimorfismo siano
assolutamente complementari.


Mi permetto di aggiungere un'osservazione: il ruolo fondamentale
dell'ereditarieta' nel riuso del codice non e' quello abusato di
ereditare da classi esistenti: nella mia esperienza questo ha senso
quasi esclusivamente quando classe base e classe derivata sono
sviluppate insieme (fanno parte dello stesso componente concettuale).
Il vero vantaggio in termini di riuso e' la possibilita' di usare una
classe in base al contratto che definisce e che magari e' effettivamente
implementato da una sotto-classe, circoscrivendo le interdipendenze agli
aspetti davvero rilevanti.
Ciao,
Nicola