Menu

Specificatori di accesso in C++: public, private, protected

Come public, private e protected decidono chi può toccare i membri di una classe in C++: la base dell'incapsulamento, con getter, setter e la via d'uscita friend.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Chi può toccare i tuoi dati

Quando hai scritto la tua prima classe, probabilmente hai esposto ogni membro al mondo esterno. Funziona, ma butta via uno dei motivi principali per cui esistono le classi: l'incapsulamento, cioè nascondere lo stato interno di una classe in modo che il resto del programma possa interagire con essa solo attraverso un'interfaccia controllata. Gli specificatori di accesso sono lo strumento con cui tracci quel confine.

Sono esattamente tre: public, private e protected. Ognuno etichetta i membri che lo seguono, e l'etichetta decide quale codice può leggerli o scriverli. Se li usi bene, la tua classe fa rispettare le proprie regole; se li usi male, qualsiasi bug in qualsiasi punto può corrompere lo stato del tuo oggetto.

I tre specificatori

Uno specificatore è una parola chiave seguita dai due punti. Ogni membro dichiarato dopo di esso, fino allo specificatore successivo, ricade in quel livello di accesso.

Togli il commento dall'ultima riga e il compilatore si rifiuta di compilare: balance è private, quindi main non può metterci mano direttamente. È proprio questo lo scopo: l'unico modo per cambiare il saldo è passare da deposit, il che significa che in seguito puoi aggiungere controlli (niente depositi negativi, log, limiti) in un solo punto ed essere certo che vengano sempre applicati.

Ecco il quadro completo:

//             accessibile da...
// public      ovunque (qualsiasi codice che abbia l'oggetto)
// private     solo i membri della classe stessa (+ friend)
// protected   i membri della classe stessa E le classi derivate (+ friend)

class e struct: il default

Puoi scrivere quanti blocchi di specificatori vuoi, in qualsiasi ordine. L'accesso dei membri prima del primo specificatore dipende da se hai usato class o struct:

  • In una class, i membri sono private per impostazione predefinita.
  • In una struct, i membri sono public per impostazione predefinita.

Questo default è l'unica differenza a livello di linguaggio tra le due parole chiave. Una struct può avere metodi, costruttori e sezioni private esattamente come una class.

Per convenzione si usa struct per semplici raggruppamenti di dati pubblici e class quando vuoi comportamento e stato nascosto, ma il compilatore non lo impone: cambia solo il default.

Incapsulamento con getter e setter

Lo schema di tutti i giorni è questo: i dati vanno in una sezione private e un metodo public ne offre un accesso controllato. Un getter di sola lettura restituisce il valore; un setter lo valida prima di assegnarlo. È qui che private ripaga.

Dato che celsius è private, non c'è modo di infilarci un valore non valido: ogni scrittura deve passare da setCelsius, che protegge l'invariante. Nota che i getter sono marcati const: promettono di non modificare l'oggetto, quindi puoi chiamarli anche su oggetti const Temperature.

protected ed ereditarietà

protected conta solo quando entra in gioco l'ereditarietà. Per il codice esterno si comporta come private, ma una classe derivata può raggiungerlo. Usalo per i membri di cui una sottoclasse ha davvero bisogno ma che il resto del programma non dovrebbe comunque vedere.

Un errore comune da principianti è usare protected su ogni membro dati "nel caso una sottoclasse ne abbia bisogno". Così allarghi silenziosamente il contratto della classe: ora ogni sottoclasse può dipendere da quel campo e tu non puoi più cambiarlo liberamente. Preferisci private e passa a protected solo quando una classe derivata ha davvero bisogno di quell'accesso.

La via d'uscita friend

A volte una singola funzione o classe esterna ha legittimamente bisogno di vedere i tuoi dettagli interni: un caso classico è l'overloading dell'operatore <<, che non puoi rendere un membro. La parola chiave friend concede a quella specifica entità l'accesso ai tuoi membri private e protected, e a nessun altro.

friend è un'eccezione voluta e chirurgica: è la classe stessa a indicare esattamente di chi si fida, quindi nessuno può concedersi l'accesso dall'esterno. Usalo con parsimonia; se ti ritrovi ad aggiungere tanti friend, probabilmente quei membri non avrebbero dovuto essere private fin dall'inizio, oppure il tuo design va ripensato.

Errori comuni da evitare

  • Rendere public ogni membro. Sembra comodo, ma perdi tutti i controlli e le invarianti che l'incapsulamento ti garantisce. Parti da dati private con metodi public.
  • Dimenticare il default di class. class Foo { int x; }; rende x private, quindi foo.x = 5 non compila. Se volevi un semplice raggruppamento di dati, usa struct o aggiungi un'etichetta public:.
  • Abusare di protected. È un confine più debole di private e ha senso solo con l'ereditarietà. Usarlo ovunque lega le sottoclassi a campi che potresti voler cambiare.
  • Aspettarsi che private sia una misura di sicurezza. È una regola in fase di compilazione che impedisce accessi accidentali, non una cifratura. I byte restano in memoria; private riguarda un design pulito, non la segretezza.

Prossimo: Struct

Hai visto che una struct è in realtà solo una class i cui membri sono public di default. La prossima pagina, sulle struct, approfondisce quando quel default pubblico è esattamente ciò che ti serve, cioè aggregati leggeri per raggruppare valori correlati, e come struct si usa nel C++ idiomatico accanto a classi complete.

Domande frequenti

Qual è la differenza tra public, private e protected in C++?

I membri public sono raggiungibili da qualsiasi punto. I membri private sono raggiungibili solo dall'interno della stessa classe (e dai suoi friend). protected è come private, ma permette di raggiungere il membro anche alle classi derivate. Per convenzione si tengono i dati private e si espone il comportamento tramite metodi public.

In C++ i membri sono private per impostazione predefinita?

In una class sì: tutto è private finché non scrivi uno specificatore di accesso. In una struct il default è public. Questo default è l'unica vera differenza tra class e struct in C++; entrambe possono avere metodi, costruttori e specificatori di accesso.

A cosa serve la parola chiave friend in C++?

friend concede a una funzione o a una classe specifica l'accesso ai tuoi membri private e protected. È un'eccezione voluta e circoscritta all'incapsulamento: la classe indica esattamente di chi si fida, quindi l'accesso non viene mai concesso in modo implicito.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA