Newsgroup: it.comp.lang.c++
Titolo: [Linguaggio] Rendere public un membro private

Ciao a tutti...
Ho dichiarato una classe: in sintesi...
class Pollo
{
private:
char * Verso;
public:
GetVerso...
SetVerso...
...
}
questa dichiarazione e' nel file Pollo.h
nel file Pollo.cpp implemento le funzioni, faccio una prova e nel file
Prova.cpp includo pollo.h, funziona tutto a dovere.
Provo a cancellare il file Pollo.cpp ed a includere il file precedentemente
generato Pollo.obj (Uso Visual C++ 6, gli obj gli includo nella pagina delle
opzioni del linker).
Avendo comunque inluso il file Pollo.h (non cancellato), il programma
continua a funzionare a dovere: mi viene il dubbio e provo ad accedere alla
variabile privata Pollo.Verso dal prmma di prova... ERRORE, lo immaginavo,
allora modifico il file Pollo.h e sostituisco la parola private con public,
compilo e... FUNZIONA!
Morale:e' possibile che i file *.obj non contengano le informazioni
necessarie a capire quale sia la reale visibilita' delle variabili?
Se faccio una classe o una libreria e decido di distribuire solo header ed
obj (il codice no), l'utente puo' modificare l'header ed accedere ai membri
privati compromettendo l'affidabilita' del codice e comunque, in ogni caso,
violando la regola della buona programmazione...
Possibile che il compilatore permetta questa schifezza? Se potete, ditemi di
no e che devo impostare una qualche opzione per la compilazione... Non ci
voglio credere!
Scusate la lunghezza, ciao Davide
/ - / - | - \ - \
Davide Piras
New (
pir...@engineer.com
)
Old (
pir...@iname.com
)
\ - \ - | - / - /

 voglio credere!
Non c'e' bisogno di preoccuparsi. Chi fa questi giochetti
crea problemi a se stesso e agli utenti del programma finale,
non a chi ha scritto le librerie.
	Mauro

voglio credere!
E` possibile tentare di aggiungere dei meccanismi di protezione 
*attivi*, ma dovresti rinunciare ad una fetta di prestazioni. 
Assolutamente non tollerabile per moltissime applicazioni...
Paolo
-----------------------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
-----------------------------------------------------------------

 necessarie a capire quale sia la reale visibilita' delle variabili?
E come potrebbe un semplice file a impedirti di modificare una porzione di
memoria?
In C++ la protezione serve solo per il design, il che vuole dire cercare di
limitare gli errori che potresti commettere imponendo (fino ad un certo
punto) un buon design.
E poi perchè ti interessa che qualcuno possa rendere public i tuoi membri
private? Mica hai dei diritti su quello che uno fa della sua RAM, e se
qualcuno vuole usare la tua libreria con uno stile di programmazione pessimo
e rischiando malfunzionamenti, perchè impedirglielo?
--
Alessandro Monti

 voglio credere!
Invece è proprio così. Le specifiche public, private e protected per i
membri di una classe valgono solamente a livello sintattico (quindi
durante la compilazione) e non sono pensate per offrire sicurezza contro
modifiche volontarie ma sono solo un'ausilio al programmatore per
controllare la correttezza del codice scritto.
EMG

Grazie Mauro, Paolo Alessandro ed Enrico per le vostre risposte...
La mia domanda e' nata in seguito ad una discussione con il professore di
ingegneria del software riguardo l'incapsulamento e la protezione:
lui sosteneva che dando i file oggetto e l'header l'utente puo' accedere
alle sole funzioni rese pubbliche senza vederne l'implementazione, a quanto
pare avevo ragione io, se l'utente "pubblica (?)" cio' che e' privato puo'
fare quello che vuole scavalcando i limiti inizialmente imposti dal
programmatore.
Il prof. sosteneva che nel file oggetto e' presente una signature o qualcosa
di simile che marca la visibilita' in modo da impedire un simile raggiro: il
Visual C++ non lo fa, chissa' gli altri compilatori...
Tutto qui, la mia domanda non aveva altri scopi...
P.S. Ma se faccio una Dll e ne distribuisco l'header e' lo stesso? e se
invece di una Dll faccio una libreria statica? (*.lib mi sembra)
Certo le Dll esportano funzioni, qui invece si tratta di accedere a
variabili e/o classi e loro definizioni...
Grazie e Ciao, Bonjour, Hello, Adiosu (in sardo!)...


O mi sbaglio?
No, non e` possibile allentare le restrizioni di accesso di un membro di
classe base (salvo alterare la classe base stessa).
Il problema in oggetto ha poco a che vedere con queste cose.
In sostanza il C++ mira a eliminare gli errori involontari, non
quelli voluti.
Paolo


Se ci accedi tramite puntatore puoi, non so in altri casi
Questo metodo pero` rientra tra le "cattiverie" volontarie!


 Mi sa che ti sbagli. Come faresti, esattamente?
Ho provato con un esempio stupidissimo:
class A{
public:
 A (){a=0;};
 int leggi_a(){return a;};
private:
 int a;
 virtual void incrementa_a (void){a++;};
};
class B:public A{
public:
 int b;
 B(){b=0;};
 void incrementa_a (void){b++;};
};
int main (){
 B b;
 int c;
 c = b.leggi_a();
 b.incrementa_a(); // E' legale, anche se era privata
 c= b.leggi_a();  // Cosa restituirà?
 return 0;
}
Comunque su "C++: manuale di stile" ci sono esempi più significativi. In
effetti io l'ho scoperto proprio da lì, prima non mi era neanche venuto in
mente di usare lo stesso nome di una funzione ereditata privata. :-)
--
Alessandro Monti

 quelli voluti.
Certo, la maggior parte almeno. Intendevo dire che è sempre possibile, più o
meno volontariamente, "gabbare" il type system e la visibilità, anche se una
programmazione attenta dovrebbe bastare ad evitarlo.
Il caso discusso invece mirava ad impedire al programmatore "malizioso"
comportamenti non desiderati, e ciò IMO non è possibile.
--
Alessandro Monti

 void incrementa_a (void){b++;};
Non stai rendendo public un membro privato di A, ma stai dichiarando
un nuovo membro in B che nasconde quello di A (ma di questo il
codice cliente non si rende conto perche` non puo` accedere ad
A::incrementa_a).
Nota che oltretutto cosi` facendo puoi incappare in seri problemi
a run time (come descritto proprio su "C++ manuale di stile")


 Ho provato con un esempio stupidissimo:
Come ho già detto, in questo caso tu _ridefinisci_ una funzione membro
non trasformi la funzione membro della classe base da privata a
pubblica. Come faresti se non avessi il sorgente della funzione stessa?
EMG

comportamenti non desiderati, e ciň IMO non č possibile.
Quelli non sono eliminabili a priori, a meno di non appesantire
incredibilmente il linguaggio, ma anche cosi` e` possibile trovare
un sistema per sconvolgere le cose. E` il prezzo da pagare per
un linguaggio che ti permetta il massimo controllo (o quasi).


 A::incrementa_a).
Appunto.
Eppure nessuno mi avvisa che sto facendo una cosa sbagliata.
Ridefinire una funzione privata non è così strano: pensa al caso di una
libreria, mica ti dovrai andare a guardare tutti i menbri private, no? Se
cerchi di scegliere nomi significativi può capitare di avere una
ridefinizione accidentale, per pigrizia.
E la classe base da canto suo probabilmente usa la funzione privata (o
protetta) per i suoi fini.
In definitiva, ottengo che un programmatore può "distruggere" la
funzionalità di una classe semplicemente derivandola.
Non sto certo dicendo che si può accedere a membri protetti, ma il thread
era partito cercando di ottenere classi "programmer-safe", e credo l'esempio
ne dimostrasse l'impossibilità.
Che mi sia espresso male? Forse si, rileggendo il post, comunque C.Pescio è
certamente più abile di me a spiegarsi, per cui avevo messo il riferimento.
--
Alessandro Monti

In conclusione :
Non e' la mia matita che disegna bene, ma la mia mano.
ZEC

certamente più abile di me a spiegarsi, per cui avevo messo il riferimento.
Infatti il libro di Pescio non e` un caso... molti errori possono essere
prevenuti dal linguaggio, ma molti altri dipendono dalle conoscenze
del programmatore. Niente e` perfetto in questo mondo, tanto meno il
C++


 funzionalità di una classe semplicemente derivandola.
Le classi con metodi virtuali sono esposte al rischio di
anomalie causate da una cattiva ridefinizione delle funzioni
virtuali, pero' e' praticamente impossibile eliminare questo
rischio senza togliere anche il supporto per il polimorfismo,
che, se usato bene, e' molto utile.
	Mauro

 funzionalità di una classe semplicemente derivandola.
Veramente non distruggi proprio niente in quanto la classe base continua
a fare riferimento alla sua funzione privata e la classe derivata, che
comunque non potrebbe utilizzare tale funzione, si definisce la sua.
EMG

 comunque non potrebbe utilizzare tale funzione, si definisce la sua.
Si, ma una funzione è privata non può essere chiamata da "fuori", ne
consegue che è usata all'interno di un altro metodo.
Ora, se la classe derivata ridefinisce tale funzione, la funzione in sè
continuerà a funzionare perfettamente, solo che qualche altra funzione della
classe derivata potrebbe smettere di farlo correttamente.
Si tratta di programmazione "suicida", ma il thread era partito cercando di
rendere impossibili situazioni come questa, no?
--
Alessandro Monti

voglio credere!
Questo e' nulla. Dovresti vedere alcuni generatori di codice (come
quello compreso nel Borland C++ 5.0x), i quali nelle classi derivate
impostano tutti i metodi come public anche se molti di questi non
dovrebbero esserlo. A mano e con molta pazienza (anche per non
spiazzare il ClassExpert) si porta tali metodi ad un livello
protected, per evitare che i programmatori accedano a dei metodi che
nella maggior parte dei casi hanno a che fare con il paint o il design
di un oggetto (e quindi non devono essere accessibili).
Ad esempio derivando la classe TButtonGadget in MyButtonGadget e
derivando il metodo GetDesiredSize il compilatore (o meglio il
ClassExpert) scrive nel file header:
//{{MyButtonGadgetVIRTUAL_BEGIN}}
  public:
    virtual void GetDesiredSize(TSize& size);
//{{MyButtonGadgetVIRTUAL_END}}
Chiaramente non ha alcun senso lasciare tale metodo di tipo public,
poiche' il programmatore non ne ricaverebbe alcun vantaggio e vi sono
invece situazioni in cui lasciare "la porta aperta" e' persino
pericoloso per il mantenimento della classe stessa.
Bye.

 rendere impossibili situazioni come questa, no?
Avevamo detto che se uno si vuole suicidare, il 
compilatore non si oppone.
Il compilatore avverte quando uno e' in pericolo
ma non lo sa.
Naturalmente ci sono opinioni molto diverse su cosa
e' pericoloso, cosa e' normale, e quando e' il caso
di avvertire.  Quasi tutti i compilatori permettono
di scegliere quali warning abilitare e quali no.
	Mauro

rendere impossibili situazioni come questa, no?
Non proprio... la domanda era un po' diversa, ma la risposta e`
che certi errori non possono essere impediti e comunque la
situazione in questione non e` in linea di principio un errore.
per if(a=b) viene emesso un warning perche` e` facile
dimenticarsi un pezzo di "==", ma generalmente ridichiarare
public una funzione che nella classe base e` privata non
puo` essere considerato una cosa involontaria...
Se derivi da una classe non e` pensabile che lo si faccia
conoscendo solo la signature dei metodi della classe base
e per giunta solo dell'interfaccia.
Ad ogni modo non e` mica detto che le scelte fatte siano tutte
prive di errori...


e per giunta solo dell'interfaccia.
Mi scuso se vado un po' fuori dal thread originario, ma ho letto 
quest'affermazione piu' volte, e non mi trova affatto d'accordo. :) 
Quello che di una classe serve sapere per derivarla dovrebbe essere : il suo 
ruolo nella gerarchia (ovvero, le relazioni di dipendenza e d'uso con altre 
classi ); l'interfaccia; la semantica dell'interfaccia (mediante 
documentazione, naming opportuno, contratti espliciti, eccetera) - cioe' il 
fatto che un certo metodo pubblico che si chiama "somma" incapsuli proprio la 
nozione di somma che ho capito; e le *relazioni* tra i suoi metodi pubblici: ad 
esempio l'ordine di invocazione, le assunzioni implicite, eccetera, tutte cose 
che si possono di solito desumere dal contratto del metodo, se c'e'.
Tanto dovrebbe bastare: imho, la conoscenza dell'implementazione (ovvero delle 
parti private) *non* e' ne' necessaria ne' auspicabile, per derivare, in un 
buon design. Una volta che so esattamente *cosa* fanno i metodi pubblici, sia 
in senso statico che dinamico, non voglio interessarmi di *come* lo facciano.
"Buone" classi si derivano su questa sola base. Se cio' provoca errori, la cosa 
puo' essere dovuta a cattivo design, a specifica incompleta dei metodi, o 
problemi strutturali del linguaggio (la cosa a cui si riferiva Alessandro Monti 
mi pare).
Giusto il mio $0.10. :)
-- 
God is Real, unless declared Integer.
ObjectZone - 
http://space.tin.it/computer/csadun/

mi pare).
Io penso che sia necessario conoscere anche cio` che riguarda la
parte privata della classe (o meglio il contratto della parte privata),
certo non bene quanto l'interfaccia pubblica e privata...
metti il caso di una member function virtuale privata, mi pare si tratti
di un caso che vada adeguatamente segnalato e documentato per evitare il
problema della fragile class. 
Per quanto riguarda l'implementazione e` chiaro che sarebbe auspicabile 
non doverne avere conoscenza alcuna (anche se capita spesso che non sia
cosi` proprio per carenza della documentazione).


 contratto della classe.
Beh, ma il contratto non dovrebbe essere legato alla parte privata, che
dovrebbe essere un dettaglio di implementazione, tant'č che in quasi tutti
gli altri OOL č totalmente nascosta.
Se un contratto dovesse dire: "attenzione, non ridefinire la funzione
TAL_DEI_TALI perchč č privata" non credi che sia una pecca del linguaggio? A
me pare che il problema sia che la parte privata di una classe in C++ non
sia poi cosě privata.
IMHO la parte privata di una classe non dovrebbe essere "toccabile"
qualunque cosa scrivi, ovviamente a meno di "trucchetti" sempre possibili,
come ridefinire l'header.
Non conosco perň le ragioni che hanno condotto ad una definizione cosě
"fragile" della parte privata di una classe.
Non discuto il fatto che sia scorretto derivare e ridefinire una funzione
membro privata come public, specie se virtual (e a proposito, che senso ha
definire virtuale una funzione privata?), ma perchč č possibile senza
neanche un warning?
--
Alessandro Monti

neanche un warning?
Infatti funzioni private (o protected) e virtuali sono una pratica 
assolutamente da evitare...
Quanto all'assenza di warning, secondo me e` improbabile
che qualcuno ridefinisca public accidentalmente una member function
privata. Per il resto poi non e` facile e/o possibile prevenire tutte
le possibili fonti di errori o solo segnalarle.


 neanche un warning?
Il senso di definire virtual una funzione e' sempre quello
di renderla ridefinibile dalle classi derivate.
Lo scopo di definire private una funzione virtuale non e'
quello di renderla intoccabile (non avrebbe senso per una
funzione virtuale), ma quello di fare in modo che possa
essere chiamata solo dai metodi della classe base.
Se una classe derivata ridefinisce la funzione come public
rende public solo la nuova funzione definida nella classe
derivata, mentre rimane impossibile chiamare la funzione
originale privata.
	Mauro

 con chissa` quali problemi.
Beh, io sono dell'opinione che non si può derivare senza dare un'occhiata
anche all'implementazione!
Almeno per le classi un tantino complesse, derivare senza prima essere
assolutamente certi di cosa si va a toccare è un po' un azzardo, non trovi?
--
Alessandro Monti

assolutamente certi di cosa si va a toccare è un po' un azzardo, non trovi?
No, su questo non sono d'accordo... l'implementazione non ti
interessa, salvo chissa` quali reconditi motivi.
A me e` capitato solo una volta una tale necessita`...


 quelli voluti.
LOL e me lo dici dopo che oggi ho passato un'ora a debuggare del codice in
cui string.append(const char *) andava in segmentation fault per un overflow
non rilevato? Si fa' per scherzare, ovviamente.. :)

non rilevato? Si fa' per scherzare, ovviamente.. :)
Non esiste un linguaggio a prova di atti di pirateria!


 Non esiste un linguaggio a prova di atti di pirateria!
Vero. E anche se ci fosse, non sarebbe divertente. Pero' ce ne sono di meno
facili da attaccare. Ovviamente, piu' lenti. :)