2010-01-19 13 views
9
class X 
{ 
protected: 
    void protectedFunction() { cout << "I am protected" ; } 
}; 

class Y : public X 
{ 
public: 
    using X::protectedFunction; 
}; 

int main() 
{ 
    Y y1; 
    y1.protectedFunction(); 
} 

In questo modo sono in grado di esporre una delle funzioni della classe base.Queste due classi violano l'incapsulamento?

  1. Questo non viola il principio di incapsulamento?
  2. C'è un motivo specifico per cui questo è in standard?
  3. C'è qualche utilizzo di questo, o verrà modificato nel nuovo standard?
  4. Ci sono problemi aperti relativi a questo nello standard?
+0

chiarire - lo scopo della mia domanda non era di parlare di modi bruti o ingannevoli per rompere l'incapsulamento. volevo solo discutere le decisioni di progettazione o alcune altre caratteristiche di C++ che potrebbero aver portato a questa funzione. È solo un effetto collaterale della soluzione al problema di nascondere la funzione o è una funzionalità indipendente introdotta in C++ per qualche motivo specifico –

+0

Penso che la ragione per cui le persone elencano tutti questi modi per interrompere l'incapsulamento non è documentare come fallo, ma per spiegare che devi scegliere di interrompere l'incapsulamento. Quando si verifica un errore perché si è tentato di accedere a un metodo oa un membro privato, ciò potrebbe servire come un delicato promemoria e, se si onorano i desideri del progettista, è necessario progettare una soluzione cooperativa. È sempre scontato che l'incapsulamento possa essere forzatamente rotto. L'incapsulamento "protetto" non può essere spezzato senza la forza, il che non credo sia un fallimento in una parte della lingua. –

risposta

12

Sì, ed è per questo che la protezione ha ricevuto una buona dose di critiche.

Bjarne Stroustrup, il creatore di C++, si rammarica questo nel suo eccellente libro The Design e l'Evoluzione di C++:

Una delle mie preoccupazioni circa protetta è esattamente questo lo rende troppo facile uso di un base comune il modo in cui si potrebbe dati globali sloppily hanno usato .... In senno di poi, penso che proteggeva è un caso in cui "buoni argomenti" e moda superato il mio giudizio migliore e le mie regole pratiche per l'accettazione nuova Caratteristiche.

+5

Ha anche detto che pensava che le funzioni protette fossero una buona idea. Il problema qui è l'uso di "utilizzo" (che suggerirei sia male) piuttosto che l'uso di "protetto". –

+0

No. Il problema con protetto è che non fornisce alcuna protezione (era l'argomento di Bjoarne). Per ottenere l'accesso al metodo tutto ciò che devi fare è ereditare dalla classe rompendo così l'incapsulamento. In affetto protetto non è più sicuro dell'uso pubblico. –

+2

@ Martin, Il libro dice funzioni protette: buone, dati protetti: cattivi. Lo hai letto davvero? –

18

L'hai fatto da solo.
Si potrebbe scrivere

class Y : public X 
{ 
public: 
    void doA() 
    { 
     protectedFunction(); 
    } 
}; 

int main() 
{ 
    Y y1; 
    y1.doA(); 
} 

non vedo alcun motivo per preoccuparsi.
Le funzioni protette sono parti di logica riutilizzabile nell'albero di ereditarietà. Puoi nasconderli se c'è qualche logica interna o restrizione o come nel tuo caso puoi esporlo se sei sicuro che ciò non danneggerà nessuno.

11

Penso che sia stato lo stesso Stroustrup a dire che le funzioni di incapsulamento e integrità dei dati incorporate in C++ sono progettate per mantenere oneste le persone oneste, non per fermare i criminali.

+4

Adoro alcune delle risposte nelle Domande frequenti sul C++ a questo tipo di domande. 'Come posso proibire agli altri di ...' - scrivi un commento dicendo così 'e come posso impedirgli effettivamente di farlo ...' - scrivi un commento affermando che saranno licenziati se lo faranno. 'Ma cosa succede se davvero, voglio davvero impedire ...' - licenziare il ragazzo. –

3

No. La funzione protetta() è protetta. Sei libero di chiamarlo da classi derivate, quindi potresti chiamarlo direttamente da un membro pubblico di Y e chiamare questo membro pubblico da main().

Se si potrebbe avere fatto con un metodo privato, ci sarebbe stato un problema ... (a cura di essere un po 'più chiaro).

+0

-1: il codice in questione non chiama il metodo protetto dal contesto della classe derivata. Il metodo protetto viene chiamato dall'ambito di main(). Se si rimuove il dichiaratore 'using', il codice non viene più compilato. –

+0

@ John, questo è ovvio! Questa è solo una scorciatoia, invece di fare da solo la funzione "trampolino". Non aumenta né diminuisce il livello di protezione della funzione. – Hexagon

+1

@John Dibling: il che significa che lo sviluppatore del programma ha esposto deliberatamente la funzione. Sarebbe stato altrettanto facile schiaffeggiare una funzione di wrapper pubblico al riguardo. Non vedo alcuna ragione per un downvote qui. –

1

Il progettista di classe deve sapere che la dichiarazione di una funzione membro o di una variabile (sebbene tutte le variabili dovrebbero essere private, in realtà) come protette è la stessa cosa che dichiararla pubblica (come hai dimostrato è facile accedere a materiale protetto). Tenendo questo a mente, ci si deve preoccupare quando si implementano tali funzioni nella classe base per tenere conto di un utilizzo "inaspettato". Non è una violazione dell'incapsulamento perché puoi accedervi ed esporlo.

protetto è solo un modo più complicato di dichiarare qualcosa di pubblico!

+0

Più come un modo per rendere possibile la dichiarazione pubblica, direi. –

2

Dal punto di vista linguistico, che non è più di una violazione di incapsulamento che creare un metodo delegato nell'oggetto derivato:

class Y : public X 
{ 
public: 
    void protectedFunction() { 
     X::protectedFunction(); 
    } 
}; 

Una volta che un metodo è protetto, esso viene offerto alle classi Derivazione a che fare con ciò che desiderano. L'intenzione per la dichiarazione using non modificava l'accesso ai metodi ereditati, ma evitava i problemi con il metodo hiding (in cui un metodo pubblico in X sarebbe nascosto nel tipo Y se è definito un sovraccarico diverso).

Ora, è un fatto che, a causa delle regole del linguaggio, può essere utilizzato per modificare il livello di accesso per i metodi "usati" come nell'esempio, ma in realtà non apre alcun buco nel sistema. Alla fine, le classi derivate non possono fare nulla di più di quello che potevano fare prima.

Se la domanda reale è se il particolare utilizzo è una violazione dell'incapsulamento dal punto di vista del design (dove il design qui è design di applicazioni, non design di linguaggio), allora molto probabilmente sì. Nello stesso modo in cui rendono pubblici i dati o i metodi interni. Se è stato progettato per essere protetti dal progettista iniziale di X, allora è probabile che Y implementazioni non voleva che fosse pubblico ... (o offrendo un metodo di delega per lo stesso scopo)

+1

sì, buona spiegazione, totalmente d'accordo –

2

No.

Per utilizzare il metodo pubblico si dovrebbe avere un esempio di Y questo non funzionerà:

int main() 
{ 
    X y1; 
    y1.protectedFunction(); 
} 

Se Y nella sua nuova definizione espone una nuova interfaccia è la Y. X è ancora protetto.

2

In C++ anche il modificatore privato non garantisce l'incapsulamento. Nessuno può impedirti di spararti ai piedi.

Herb Sutter nel suo articolo "Uses and Abuses of Access Rights" illustra diversi modi in cui è possibile "interrompere l'incapsulamento".

Ecco un esempio divertente dell'articolo di Herb.

Male macro magia

#define protected public // illegal 
#include "X.h" 
//... 
X y1; 
y1.protectedFunction(); 
+1

Questo è un comportamento indefinito, in parte perché infrange la regola di una definizione. Anche se la ridefinizione di "protetto" era legale, non si poteva contare sul fatto che i membri della classe fossero nello stesso ordine. –

+1

Sì, conosco il comportamento non definito. Voglio dimostrare che QUESTO è un trucco, che "rompi l'incapsulatino", ma non lo è il wrapping di fuction protected. –

1

No, io non vedo proprio il problema.

Invece di using, che avrebbe potuto fare questo:

class X 
{ 
protected: 
    void protectedFunction() { cout << "i am protected" ; } 
}; 

class Y : public X 
{ 
public: 
    void protectedFunction() { X::protectedFunction(); } 
}; 

Ogni classe può prendere qualsiasi membro che è visibile ad esso, e decidere di esporre pubblicamente. Può essere un cattivo design di classe farlo, ma non è certamente un difetto nella lingua. L'intero punto in membri privati ​​o protetti è che la classe stessa deve decidere chi dovrebbe ottenere l'accesso al membro. E se la classe decide "darò l'accesso al mondo intero", allora è così che la classe è progettata.

Se seguiamo la logica, quindi getter e setter violano anche l'incapsulamento. E a volte lo fanno. Ma non perché la lingua è rotta. Semplicemente perché stai scegliendo di progettare classi rotte.

Rendendo protetto un membro, si danno alle classi derivate la libertà di fare a piacimento con il membro. Possono vederlo, così possono modificarlo o esporlo pubblicamente. Hai scelto di renderlo possibile quando hai reso il membro protetto. Se non lo volevi, avresti dovuto renderlo privato.

1

Personalmente penso che questa domanda debba essere risolta come una questione di design piuttosto che una domanda di tecnologia.

Vorrei chiedere, "Qual è stato il punto del metodo protetto in primo luogo?" Era un metodo che solo le sottoclassi dovrebbero chiamare, o è un metodo che le sottoclassi dovrebbero ignorare? Tuttavia, potrebbe trattarsi di un metodo che non è previsto in una classe base generica, ma che potrebbe essere previsto in una sottoclasse. Al cliente della classe base non hanno mai saputo di quel metodo protetto. Il creatore della sottoclasse ha scelto di espandere il contratto e ora il metodo protetto è pubblico. La domanda in realtà non dovrebbe essere consentita dal C++, ma è giusto per te, manutentori di classe, contratto e futuro. Certo potrebbe essere un cattivo odore, ma in realtà è necessario renderlo giusto per il caso d'uso in questione.

Se si rende pubblico il metodo protetto, assicurarsi di fornire correttamente la documentazione interna al manutentore che spieghi il motivo per cui viene presa questa particolare decisione.

In generale, tuttavia, per ragioni di sicurezza come menzionato precedentemente, è probabile che si desideri utilizzare una funzione delegato (con un nome diverso) nella sottoclasse. Quindi invece di "get (int)" potresti avere "getAtIndex (int)" o qualcosa del genere. Questo ti permette di rifare/proteggere/astrarre/documentare più facilmente in futuro.

Problemi correlati