Forse un esempio può aiutare a spiegare i vantaggi della programmazione orientata agli oggetti.
Considera una lista collegata, costituita da strutture di nodi contenenti alcuni dati e che punta ad altri nodi:
struct Node {
Node* next;
int data;
}
Assumi un elenco di questi (li ho nominati per comodità):
Node c{null, 4};
Node b{&c, 73};
Node a{&b, 42};
Node* list = &a;
Puoi usare questo elenco come segue:
std::cout << list->data; // prints 42
std::cout << list->next->data; // prints 4
Tutto va bene qui, ma immagina da qualche parte nel tuo programma che succede:
Node d{&a, 17};
list->next->next = &d;
Ora la lista è incasinata, è: [a, b, d, a, b, d, …]
; il nodo c
è perso e l'elenco è diventato una ripetizione infinita.
1a
Per rispondere alla prima parte della parte 1 della tua domanda, quando Node::next
è privato, l'assegnazione problematica list->next->next = &d;
non può avvenire.
1b & 2
Dato un setter pubblico:
Nodo void :: setNext (Node * new_next) {
this- > next = new_next;
}
Come dici tu, chiunque può usare solo list->next->setNext(&d);
. Questo porta alla risposta alla seconda parte della tua domanda: avere un setter setNext()
è cattivo. Le liste dovrebbero avere operazioni di modifica come insert(value)
, append(value)
e remove(value)
.
Questo è un esempio della regola generale: le classi dovrebbero avere operazioni che si adattano al significato e all'uso previsti, non a getter e setter per ogni campo che l'implementazione ha. Oppure la versione che leggi: "Non chiedere le informazioni che ti servono per fare il lavoro, chiedi all'oggetto che ha le informazioni per fare il lavoro per te."
3
La tua domanda, "Non capisco perché implementare oggetti sia più efficiente di solo se-istruzioni e chiamate di funzione", è difficile. Innanzitutto la parola "efficiente" può avere due significati:
- i programmi vengono eseguiti più velocemente e / o consumano meno risorse
- scrivere un programma richiede meno sforzo
Per il primo punto che esegue più velocemente o consuma meno risorse, sia i programmi orientati agli oggetti che i programmi che usano le istruzioni if e le chiamate di funzione possono essere efficienti in questo senso o molto inefficienti. Per alcuni problemi a pensarli in modo orientato agli oggetti, si ottiene una soluzione più elegante ed efficiente rispetto alle istruzioni if e alle chiamate di funzione. Per altri problemi, la programmazione orientata agli oggetti si intromette e causa un sovraccarico.
Sulla seconda interpretazione di "efficiente": scrivere un programma richiede meno sforzo.
- Poiché @JB_King afferma che l'orientamento all'oggetto aiuta a organizzare le cose che appartengono insieme.
- Una volta che hai la classe list, in altre parti del codice puoi pensare in termini di mantenimento di un elenco di "x" e invio di un elenco di "y", anziché manipolazioni di puntatori. Inoltre, quando si guarda la libreria standard C ++, la libreria runtime Java, Boost Collection , raccolte Commons , Google guava , e altri scoprirai che altri hanno già scritto delle classi List e che forse per il tuo problema di programmazione un elenco non è la migliore, ma qualche altro tipo di raccolta.
Quindi una volta che hai (o qualcun altro ha) scritto un corso puoi riutilizzarlo e non devi scriverlo più.
NOTA: ovviamente lo stesso vale per le buone librerie di funzioni, alla mia risposta manca una buona spiegazione del perché l'object oriented è meglio riusabile rispetto alle librerie funzionali.
Elaborazioni e nodi laterali
Anche se decidi che questa classe elenco richiede realmente un metodo setNext()
-method, allora il metodo può avere vantaggi rispetto alla manipolazione diretta del Node::next
-field. Il metodo può eseguire controlli aggiuntivi; per esempio:.
Node: setNext (Nodo * newNext) {
se (this- > next == null)
{
this- > next = newNext;
}
altro
{
lanciare un nuovo errore ();
}
}
Rendere privati i campi non impedisce a programmatori / codice malintenzionati di leggere e manipolare il campo; cioè.
class SecurityController {
private:
std::string secretPassword;
};
e
struct SecurityController2 {
std::string secretPassword;
}
sono entrambi ugualmente (in) sicuri.
Dichiarare le cose private (e altri metodi per nascondere i dettagli di implementazione) aiuta solo i programmatori che si comportano bene a creare un codice migliore.
L'inizializzazione
A volte l'inizializzazione di un oggetto è l'unica volta in cui è richiesto l'accesso diretto ai campi di un oggetto. Quindi puoi definire un costruttore che accetta e imposta il campo, ad esempio:
Node::Node(int value_) {
this->value = value_;
}
Tuttavia, spesso esistono costruttori migliori che non richiedono che il chiamante fornisca valori. Ad esempio:
List::List() {
…
}
per costruire un List
vuoto e
List::List(std::iterator<int> start, std::iterator<int> end) {
…
}
per creare un elenco contenente copie dei valori dell'intervallo STL di C ++ [inizio, fine >.