Newsgroup: it.comp.lang.c++
Titolo: Problema con "protected"

Salve a tutti,
vi riporto un esempio della seconda edizione de "Il linguaggio C++" di
Soustrup dove ho avuto un problema: nel capito 7 a pagina 227 č
riportato questo esempio:
class X
{
  public:
  int a;
};
class Y1: public X{};
class Y2: protected X{};
class Y3: private X{};
class Z2: public Y2{ void f(Y1*, Y2*, Y3*); };
void Z2::f(Y1*py1, Y2*py2, Y3*py3)
{
  X *px=py1;  //Ok
  py1->a=7;   //Ok
  px=py2;     //Ok
  py2->a=7;   //Ok
  px=py3;     //error
  py3->a=7;   //error
}
Il mio problema č che la riga: 
py2->a=7;
nella funzione mi viene segnalata come errore dal compilatore g++ mentre
sul libro viene indicata come Ok.
Ora secondo me X::a č un membro privato di Y2 in quanto la base X č
protected per Y2 quindi il compilatore ha ragione a darmi un errore.
E' cosě, ovvero c'č un banale errore sul libro o non ho capito un pň di
cose?
Grazie molte per le eventuali risposte.
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 protected per Y2 quindi il compilatore ha ragione a darmi un errore.
Primo, protected != private. Le classi derivate possono accedere ai 
membri protected delle classi base.
Secondo, X non è protected per Y2. E' protected per chi /si serve/ di Y2.
La riga
class Y2: protected X{};
indica concettualmente che i membri della classe X devono essere 
inseriti nella sezione "protected" di Y2.
Indicando il "contenuto" di X con cX_?, ottieni qualcosa del genere:
class Y2 /*: protected X */
{
protected:
   cX_public
   cX_protected
   cX_private
};
cX_public è public o protected?
L'ambiguità viene risolta scegliendo la qualifica più restrittiva.
Allora nella classe Y2, cX_public è protected, cX_protected è protected 
e cX_private è private (non protected, perché meno restrittivo!).
Kiuhnm

e cX_private è private (non protected, perché meno restrittivo!).
In sintesi, l'esempio del libro contiene un errore? A me sembra di si.
PS Non mi interessa dimostrare l'errore, mi interessa capire a fondo.
Grazie per la risposta!!


 PS Non mi interessa dimostrare l'errore, mi interessa capire a fondo.
Allora perché non hai letto /con attenzione/ la spiegazione che ti ho 
dato nel messaggio precedente?
Kiuhnm

 E' così o no?
Dipende.
Se i è pubblico, allora si.
Se è privato o protetto allora no.
Se è protetto puoi accedere solo dall'interno della classe o da una sua 
derivata (e solo attraverso un puntatore alla derivata stessa).
Esempio:
class X {
protected:
   int i;
};
class Y: public X {
   void f(Y*y, X*x)
   {
      int yi = y->i; // ok
      int xi = x->i; // errore
   }
};

Alla luce di quanto detto, ripropongo l'esempio in cui vi segnalo
le mie perplessità:
class X
{
  public:
  int a;
};
class Y1: public X{};
class Y2: protected X{};
class Z2: public Y2{ void f(Y1*, Y2*, Y3*); };
void Z2::f(Y1*py1, Y2*py2, Y3*py3)
{
  X *px=py1;  
  py1->a=7;   
  px=py2;     
  py2->a=7;		//La classe Z2 è derivata da Y2 e non da X, quindi in Z2,
come
                        //in ogni altro punto che non sia una funzione
propria o amica
                        //di Y2 io non posso accedere direttamente
all'elmento 'a' perchè
			//questo è un protected di Y2.
			//Non è così?! L'esempio è sbagliato, questa riga non è corretta.
  px=py3;     
  py3->a=7;   
}
Grazie a tutti!


 			//Non è così?! L'esempio è sbagliato, questa riga non è corretta.
La derivazione e' una proprieta' transitiva! Se Y2 deriva da X e Z2
deriva da Y2, Z2 deriva da X. E' piu' chiaro adesso?
Ciao,
Nicola

provo a dire la mia:
secondo me ti confondi tra due casi distinti:
esempio:
class X {
	private:
		int i;
	public:
		// qui tutto OK, posso accedere a i
		int foo() { i = 10; return i; }
};
int main()
{
	X mia_x;
	// qui NO!!! i e' privata
	mia_x.i = 10;
	return 0;
}
La stessa cosa vale con le derivazioni. Se l'elemenento e' protected hai
accesso dalle funzioni membro, ma non dall'esterno della classe.
chiaro?

 chiaro?
I membri "protected" sono accessibili per definizione dalle funzioni
proprie della
classe, dalle funzioni amiche della classe e dalle stesse (proprie ed
amiche) delle
classi derivate.
Questo non dovrebbe avvalorare il fatto che non posso scrivere
"py2->a=7;"?!?!?
Grazie.


 postato in un compilatore?!?! 
E tu hai provato a leggere veramente le risposte?
Probabilmente hai letto il libro di Stroustrup con la stessa attenzione 
con cui hai letto le risposte che ti abbiamo dato.
Kiuhnm

 Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG
L'information-hiding in C++ e' a livello di classe, non di oggetto.
Quindi se richiami "py2->a=7;" all'interno di una classe che puo'
accedere ad 'a' lo puoi fare anche tramite un oggetto che non sia this.
Ciao


Cordialissimo e per niente acido Kiuhnm, nella speranza che lei non 
sia occupato nel progetto di Windows XPBis la importuno per la mia
ultima
domanda. Sia ben chiaro, lo faccio solo per la mia povera madre che
piange
disperata urlando per aver solo sprecato soldi nel tentativo di farmi
studiare.
In definitiva, hai fatto copia ed incolla di quel codice? Perchè ho un
errore?
Fidandomi della tua prima risposta (letta con molta attenzione!) ma
continuando
a rilevare l'errore vorrei capire dove sta il problema (oltre che nel
mio
cervello :P ).
Ti saluto e ti ringrazio sperando che non te la prenda per la mia
ironia.


 Perchè ho un errore?
Perché il compilatore non è standard-compliant.
Kiuhnm


Per la verità è un pezzo che ci penso. E credo che il compilatore abbia 
ragione e che il libro sia in errore.
In particolare a causa del paragrafo 11.5 dello standard.
Con riferimento al codice nell'OP, il membro "a" è protetto in "Y2". 
"Z2" deriva da "Y2" e quindi ha accesso ad "a" ma SOLO se lo fa tramite 
un puntatore o reference a "Z2" o una classe da essa derivata. Non se lo 
fa tramite un puntatore a "Y2" (infatti non c'è certezza che l'Y2 
puntato sia effettivamente un sottooggetto di una Z2).
Credo che _neo_ dopo tutto abbia perfettamente ragione.

 Credo che _neo_ dopo tutto abbia perfettamente ragione.
Corto circuito mentale. Leggi la mia prima risposta e vedrai che la 
spiegazione dà ragione al compilatore. Il problema è che non me ne sono 
accorto nemmeno io :)
Le mie più sentite scuse a Neo :(
Kiuhnm

 Kiuhnm
Be, almeno la mia mamma non si dispererà per i soldi spesi inutilmente
per
farmi studiare :P
Cmq scherzi a parte, grazie.
:)
_neo_Programmer


 accorto nemmeno io :)
Gia', ma quale compilatore? Cosa curiosa dei quattro compilatori coi
quali ho provato il programma allegato i due ritenuti piu' conformanti
(Comeau e VC++ 8.0) non danno errori, mentre i due "piu' vecchi" (BCB6
e g++ 3.4.2) si.
Comunque nel frattempo anche Stroustrup se n'e' accorto:
http://www.research.att.com/~bs/3rd_printing15.html
Ciao,
Nicola
class X
{
  public:
      int a;
};
class Y1: public X{};
class Y2: protected X{};
class Y3: private X{};
class Z2: public Y2
{
    public:
        void f(Y1*, Y2*, Y3*);
};
void Z2::f(Y1*py1, Y2*py2, Y3*py3)
{
  X *px=py1;  //Ok
  py1->a=7;   //Ok
  px=py2;     //Ok
  py2->a=7;   //Ok
//  px=py3;     //error
//  py3->a=7;   //error
}
int main()
{
    Y1 y1;
    Y2 y2;
    Y3 y3;
    Z2 z2;
    z2.f(&y1, &y2, &y3);
}

 
http://www.research.att.com/~bs/3rd_printing15.html
La spiegazione che dà non mi soddisfa per niente.
Penso anche che lo standard sia scritto molto male perché risulta quasi 
completamente privo di organicità.
Non mi stupisco che gli implementatori di compilatori facciano fatica ad 
attenervisi.
Bisognerebbe iniziare a parlare di rifattorizzazione degli standard.
Kiuhnm

 La spiegazione che dà non mi soddisfa per niente.
Perché no?
E' scritta un maniera molto più succinta, ma in pratica è la stessa che 
ho dato io:
 > Con riferimento al codice nell'OP, il membro "a" è protetto in "Y2".
 > "Z2" deriva da "Y2" e quindi ha accesso ad "a" ma SOLO se lo fa
 > tramite un puntatore o reference a "Z2" o una classe da essa derivata.
 > Non se lo fa tramite un puntatore a "Y2" (infatti non c'è certezza che
 > l'Y2 puntato sia effettivamente un sottooggetto di una Z2).
La prima parte è proprio identica. Nella seconda lui dice che non 
sappiamio che il puntatore punta a uno Z2 e io dico che l'accesso non 
avviene tramite puntatore o reference a Z2 (è la stessa cosa). Lui 
esplicita che se l'oggetto puntato non è uno Z2 non sappiamo come Y2::X 
viene usato, io lo lascio come conseguenza implicita del fatto che per 
accedervi dobbiamo essere certi che l'oggetto puntato sia un 
sottooggetto di Z2, ma la sostanza è la stessa.

 viene usato, [..]
Quali sono questi modi di usare Y2::X?
Kiuhnm


Y2::X è protetto in Y2. Quindi è accessibile dalle sue derivate.
Supponi di avere un'altra classe:
class Z3: public Y2 {
   // bla bla
};
Il modo in cui Z3 utilizza Y2:X (che le è accessibile, nelle sue 
istanze) può essere molto diverso dal modo in cui lo usa Z2.
Per questo un metodo di Z2 non può accedere a uno Z3::Y2::X neanche 
tramite un puntatore a Y2.
E' il motivo per cui esiste il paragrafo 11.5 dello standard.

 istanze) può essere molto diverso dal modo in cui lo usa Z2.
Si può dire la stessa cosa anche se Y2 deriva X pubblicamente.
Considera
B {a}
A : public B
C : public B
Come possiamo permettere a tutti di accedere ad /a/ tramite puntatore o 
reference a B senza sapere come tale istanza di B venga effettivamente 
usata?
Kiuhnm


Possiamo perché il fatto stesso che la derivazione sia pubblica implica 
la presenza dell'interfaccia.
Una interfaccia pubblica è per definizione conosciuta. Ovvero per 
definizione sappiamo come funziona e come viene usata.
Una interfaccia protetta è conosciuta solo alla classe e alle sue derivate.
Una interfaccia privata è conosciuta solo alla classe stessa.
Può sembrare strano ma in pratica:
B:
Y2: protected B
Z2: public Y2
All'interno di un metodo di Z2 un puntatore ad uno Z2 punta ad un 
oggetto che possiede tutte le caratteristiche pubbliche e protette di Y2 
e di B. Ma un puntatore a Y2 possiede solo le caratteristiche pubbliche 
di Y2. In pratica un metodo di Z2 quando usa un puntatore a Y2 NON SA 
che Y2 deriva da B (perché la derivazione è protetta).

 la presenza dell'interfaccia.
Ci ho pensato anch'io subito dopo, infatti.
Mi concentravo solo sull'aspetto strettamente tecnico senza pensare al 
concetto d'interfaccia.
Kiuhnm