2010-11-02 30 views
14

Sto programmando in C++ in questo momento e adoro usare i puntatori. Ma sembra che altri linguaggi più recenti come Java, C# e Python non ti permettano di dichiarare esplicitamente i puntatori. In altre parole, non è possibile scrivere sia int x e int * y, sia con x un valore mentre è un puntatore, in una di queste lingue. Qual è il ragionamento dietro a questo?I puntatori sono cattivi?

+1

la maggior parte delle persone direbbe che possono essere pericolose, a causa della possibilità di rovinare e creare errori di seg o perdite di memoria. – Jim

+6

Cosa stai facendo con i puntatori che ami? – GManNickG

+0

@ GMan - Penso che la mia cosa preferita sia permettere a una classe 'A' di contenere puntatori ad un'altra classe' B', così che se un oggetto di 'B' è in realtà una classe' C' ereditata da 'B', allora 'A' può chiamare le funzioni di' C' invece delle funzioni virtuali di 'B'. Suppongo che altre lingue potrebbero essere implementate in modo diverso per evitare quella necessità di puntatori. Inoltre, puoi fare in modo che due classi includano i puntatori l'una all'altra in modo che entrambi possano accedere a ciascuna funzione, ma non puoi farlo con i valori perché creerebbe un ciclo infinito con la classe 'A' che contiene la classe' B' che contiene la classe 'A '... – wrongusername

risposta

17

I puntatori non sono male, sono semplicemente facili da sbagliare. Nelle lingue più recenti hanno trovato il modo di fare le stesse cose, ma con meno rischi di spararti ai piedi.

Non c'è niente di sbagliato con i puntatori però. Vai avanti e amale.

Verso il tuo esempio, perché vuoi che sia xey che puntino alla stessa memoria? Perché non chiamarlo sempre x?

Un altro punto, i puntatori indicano che è necessario gestire da soli la durata della memoria. Le lingue più recenti preferiscono utilizzare la garbage collection per gestire la memoria e consentire che i puntatori rendano tale compito piuttosto difficile.

+0

Beh, non voglio dire che 'x' e' y' dovrebbero puntare allo stesso indirizzo di memoria per sempre, solo che non puoi dichiarare 'x' come memorizzare un valore e' y' come memorizzare un indirizzo di memoria. Non so, però, preferisco essere sempre in grado di scegliere esplicitamente tra dichiarare una variabile con un valore o un riferimento. – wrongusername

+12

Ovviamente, niente di buono viene gratis. I puntatori C++ sono potenti e pericolosi, e in alcune delle altre lingue, mentre non puoi spararti ai piedi, hai difficoltà a mirare affatto. Ci sono cose da imparare da come i linguaggi di livello superiore gestiscono i puntatori che possono essere applicati a C++. – ssube

+0

@peachykeen: +1 per la metafora intelligente. – Cam

6

I puntatori possono essere sfruttati e le lingue gestite preferiscono proteggerti dalle potenziali insidie. Tuttavia, i puntatori non sono certamente negativi - sono una caratteristica integrale dei linguaggi C e C++ e scrivere codice C/C++ senza di essi è al contempo complicato e ingombrante.

12

I'll start with one of my favorite Scott Meyers quotes:

Quando do colloqui sulla gestione delle eccezioni, insegno la gente due cose:

  • puntatori sono i vostri nemici, perché portano ai tipi di problemi che auto_ptr è stato progettato eliminare.

  • POINTERS SONO I TUOI AMICI, perché le operazioni sui puntatori non possono essere lanciate.

Poi dire loro di avere un giorno piacevole :-)


Il punto è che i puntatori sono estremamente utili ed è certamente necessario capire loro durante la programmazione in C++. Non puoi capire il modello di memoria C++ senza capire i puntatori. Quando si implementa una classe proprietaria di risorse (ad esempio un puntatore intelligente, ad esempio), è necessario utilizzare i puntatori e si può sfruttare la garanzia di non lancio per scrivere classi proprietarie di risorse eccezionalmente sicure.

Tuttavia, nel codice dell'applicazione C++ ben scritto, non si dovrebbe mai dovere lavorare con i puntatori grezzi. Mai. È consigliabile utilizzare sempre un livello di astrazione invece di lavorare direttamente con i puntatori:

  • Utilizzare riferimenti anziché puntatori laddove possibile. I riferimenti non possono essere nulli e rendono il codice più facile da capire, più facile da scrivere e più facile da revisionare.

  • Utilizzare puntatori intelligenti per gestire qualsiasi puntatore che si utilizza. Puntatori intelligenti come shared_ptr, auto_ptr e unique_ptr consentono di evitare perdite di risorse o risorse prematuramente.

  • Utilizzare contenitori simili a quelli presenti nella libreria standard per l'archiviazione di raccolte di oggetti anziché allocare gli array autonomamente. Utilizzando contenitori come vector e map, è possibile garantire che il codice sia protetto da eccezioni (nel senso che anche quando viene generata un'eccezione, non si verificheranno perdite di risorse).

  • Utilizzare gli iteratori quando si lavora con i contenitori. È molto più semplice utilizzare gli iteratori correttamente rispetto a utilizzare correttamente i puntatori e molte implementazioni di librerie forniscono il supporto per il debug per aiutarti a scoprire dove li stai utilizzando in modo errato.

  • Quando si utilizzano API legacy o di terze parti e si devono assolutamente utilizzare i puntatori non elaborati, scrivere una classe per incapsulare l'utilizzo di tale API.

C++ ha la gestione automatica delle risorse sotto forma di Scope-Bound Resource Management (SBRM, detta anche delle risorse di acquisizione è di inizializzazione, o Raii). Usalo. Se non lo stai usando, lo stai facendo male.

+1

I riferimenti possono essere "null", ad esempio quando si fa riferimento a un puntatore non valido non assegnato. Vedi: http://codepad.org/JEPQpv2v – Paul

+8

@Paul: il comportamento dello snippet di codice non è definito. "Un riferimento null non può esistere in un programma ben definito, perché l'unico modo per creare tale riferimento sarebbe quello di associarlo all '" oggetto "ottenuto dereferenziando un puntatore nullo, che causa un comportamento non definito" (C++ 03 8.3.2/4). –

+1

@James McNellis: anche l'accesso a un puntatore nullo è un comportamento non definito, ma questo non è il punto. Il punto era, è che è completamente possibile avere un riferimento "non valido" (cioè "null"/buco nero di comportamento non definito/ecc.). Potrebbe raggiungere un po ', ma è sicuramente divertente eseguire il debug quando viene rilevato ... – Paul

3

Un vero "puntatore" ha due caratteristiche.

  • Esso contiene l'indirizzo di un altro oggetto (o primitiva)
    • ed espone la natura numerica di tale indirizzo in modo da poter fare l'aritmetica.

Tipicamente le operazioni aritmetiche definite per i puntatori sono:

  1. Aggiungendo un numero intero a un puntatore in un array, che restituisce l'indirizzo di un altro elemento.
  2. Sottrazione di due puntatori nello stesso array, che restituisce il numero di elementi intermedi (comprensivi di un'estremità).
  3. Confronto di due puntatori nello stesso array, che indica quale elemento è più vicino alla testa dell'array.

Le lingue gestite generalmente ti portano lungo la strada dei "riferimenti" anziché dei puntatori. Un riferimento contiene anche l'indirizzo di un altro oggetto (o primitivo), ma l'aritmetica non è consentita.

Tra le altre cose, ciò significa che non è possibile utilizzare l'aritmetica del puntatore per allontanarsi dalla fine di un array e trattare altri dati utilizzando il tipo sbagliato. L'altro modo di formare un puntatore non valido viene preso in considerazione in tali ambienti utilizzando la garbage collection.

Insieme questo garantisce la sicurezza del tipo, ma con una terribile perdita di generalità.

+0

Non vedo come si perde la generalità. Non avere i puntatori ti costringe a chiamare una vanga come una vanga: se vuoi una serie di elementi, la dichiari come una matrice di elementi e usi indici interi, che * possono * essere manipolati numericamente. (Si noti che in C e C++, l'aritmetica del puntatore è formalmente consentita SOLO tra i puntatori che puntano nello stesso blocco di memoria, cioè la matrice). – zvrba

+0

@zvrba: il fatto che i linguaggi gestiti non ti permettano di creare un riferimento a un sottobobito è un'enorme perdita di generalità.Non essere in grado di digitare-pun per char * è una perdita enorme di generalità. Inoltre, la mia risposta sottolinea che entrambi i puntatori devono essere nello stesso blocco. Tuttavia, i puntatori a oggetti diversi hanno un ordine non definito ma ** stabile **, negli ambienti gestiti questa garanzia non viene più effettuata, il garbage collector non è autorizzato a spostare oggetti ma a modificare il loro ordine. –

+0

Cosa intendi per "perdita di generalità"? E i cast non sicuri (ad esempio, per char *) non sono una proprietà intrinseca dei puntatori, è solo una caratteristica C. – zvrba

3

cerco di rispondere direttamente alla domanda di OP:

In altre parole, non è possibile scrivere sia int x e int * y, e hanno x essere un valore mentre y è un puntatore, in c'è ne di quelle lingue. Qual è il ragionamento dietro a questo?

Il motivo dietro questo è il modello di memoria gestita in queste lingue.In C# (o Python, o Java, ...) la durata delle risorse e quindi l'utilizzo della memoria sono gestiti automaticamente dal runtime sottostante o dal suo n. garbage collector, per essere precisi. In breve: l'applicazione non ha alcun controllo sulla posizione di una risorsa in memoria. Non è specificato - e non è nemmeno garantito che rimanga costante durante la vita di una risorsa. Quindi, la nozione di puntatore come "una posizione di qualcosa nella memoria virtuale o fisica" è completamente irrilevante.

0

Come qualcuno ha già menzionato, i puntatori possono, e in realtà, andranno male se si dispone di un'applicazione massiccia. Questo è uno dei motivi per cui a volte vediamo Windows avere problemi a causa di puntatori NULL creati! Personalmente non mi piacciono i puntatori perché causano una perdita di memoria terribile e non importa quanto tu gestisca bene la tua memoria, alla fine ti darà la caccia in qualche modo. Ho sperimentato molto questo con OpenCV quando lavoravo intorno alle applicazioni di elaborazione delle immagini. Avere un sacco di puntatori in giro, metterli in una lista e poi recuperarli in seguito ha causato problemi per me. Ma di nuovo, ci sono buoni lati nell'usare i puntatori e spesso è un buon modo per mettere a punto il tuo codice. Tutto dipende da cosa stai facendo, quali specifiche devi soddisfare, ecc.

Problemi correlati