Perché dovrei usare un puntatore piuttosto che l’object stesso?

Vengo da uno sfondo Java e ho iniziato a lavorare con gli oggetti in C ++. Ma una cosa che mi è venuta in mente è che spesso le persone usano puntatori agli oggetti piuttosto che agli oggetti stessi, ad esempio questa dichiarazione:

Object *myObject = new Object; 

piuttosto che:

 Object myObject; 

O invece di usare una funzione, diciamo testFunc() , in questo modo:

 myObject.testFunc(); 

dobbiamo scrivere:

 myObject->testFunc(); 

Ma non riesco a capire perché dovremmo farlo in questo modo. Suppongo che abbia a che fare con efficienza e velocità poiché otteniamo l’accesso diretto all’indirizzo di memoria. Ho ragione?

È molto spiacevole vedere l’allocazione dynamic così spesso. Questo mostra solo quanti programmatori C ++ non ci sono.

In un certo senso, hai due domande raggruppate in una. Il primo è quando dovremmo usare l’allocazione dynamic (usando new )? Il secondo è quando dovremmo usare i puntatori?

L’importante messaggio da portare a casa è che devi sempre usare lo strumento appropriato per il lavoro . In quasi tutte le situazioni, c’è qualcosa di più appropriato e più sicuro che eseguire l’allocazione dynamic manuale e / o usare puntatori grezzi.

Allocazione dynamic

Nella tua domanda hai dimostrato due modi per creare un object. La differenza principale è la durata di archiviazione dell’object. Quando si esegue Object myObject; all’interno di un blocco, l’object viene creato con la durata di archiviazione automatica, il che significa che verrà distrutto automaticamente quando non sarà più disponibile. Quando esegui un new Object() , l’object ha durata di archiviazione dynamic, il che significa che rimane attivo finché non lo delete esplicitamente. Dovresti utilizzare la durata di archiviazione dynamic solo quando ne hai bisogno. Cioè, dovresti sempre preferire la creazione di oggetti con durata di archiviazione automatica quando puoi .

Le due principali situazioni in cui è ansible richiedere l’allocazione dynamic:

  1. È necessario che l’object superi l’ambito corrente, quell’object specifico in quella posizione di memoria specifica, non una copia di esso. Se stai bene copiando / spostando l’object (il più delle volte dovresti essere), dovresti preferire un object automatico.
  2. È necessario allocare molta memoria , che può facilmente riempire lo stack. Sarebbe bello se non dovessimo preoccuparci di questo (il più delle volte non dovresti farlo), dato che è davvero al di fuori della portata del C ++, ma sfortunatamente dobbiamo fare i conti con la realtà dei sistemi stai sviluppando per.

Quando si richiede assolutamente l’allocazione dynamic, è necessario racchiuderlo in un puntatore intelligente o in un altro tipo che esegue RAII (come i contenitori standard). I puntatori intelligenti forniscono la semantica della proprietà di oggetti allocati dynamicmente. Dai un’occhiata a std::unique_ptr e std::shared_ptr , per esempio. Se li usi in modo appropriato, puoi quasi completamente evitare di eseguire la gestione della tua memoria (vedi la Regola dello zero ).

puntatori

Tuttavia, ci sono altri usi più generali per i puntatori raw oltre l’allocazione dynamic, ma la maggior parte ha alternative che dovresti preferire. Come prima, preferisci sempre le alternative a meno che tu non abbia davvero bisogno di indicazioni .

  1. Hai bisogno di semantica di riferimento . A volte vuoi passare un object usando un puntatore (indipendentemente da come è stato allocato) perché vuoi che la funzione a cui stai passando abbia accesso a quell’object specifico (non una sua copia). Tuttavia, nella maggior parte dei casi, dovresti preferire i tipi di riferimento ai puntatori, perché questo è specificamente per cosa sono progettati. Si noti che non si tratta necessariamente di estendere la durata dell’object oltre l’ambito corrente, come nella situazione 1 precedente. Come prima, se stai bene passando una copia dell’object, non hai bisogno di semantica di riferimento.

  2. Hai bisogno di polimorfismo . È ansible chiamare le funzioni solo in modo polimorfico (ovvero, in base al tipo dinamico di un object) tramite un puntatore o un riferimento all’object. Se questo è il comportamento di cui hai bisogno, allora devi usare puntatori o riferimenti. Ancora una volta, i riferimenti dovrebbero essere preferiti.

  3. Si vuole rappresentare che un object è facoltativo consentendo il passaggio di un nullptr quando l’object viene omesso. Se si tratta di un argomento, si consiglia di utilizzare argomenti predefiniti o sovraccarichi di funzioni. Altrimenti, dovresti preferire usare un tipo che incapsula questo comportamento, come std::optional (introdotto in C ++ 17 – con gli standard C ++ precedenti, usa boost::optional ).

  4. Desiderate disaccoppiare le unità di compilazione per migliorare i tempi di compilazione . La proprietà utile di un puntatore è che richiede solo una dichiarazione diretta del tipo puntato (per utilizzare effettivamente l’object, è necessaria una definizione). Ciò consente di disaccoppiare parti del processo di compilazione, il che potrebbe migliorare significativamente i tempi di compilazione. Vedi l’ idioma Pimpl .

  5. È necessario interfacciarsi con una libreria C o una libreria in stile C. A questo punto, sei costretto a usare i puntatori grezzi. La cosa migliore che puoi fare è assicurarti di lasciar perdere i tuoi puntatori grezzi all’ultimo momento ansible. È ansible ottenere un puntatore raw da un puntatore intelligente, ad esempio, utilizzando la sua funzione get member. Se una libreria esegue alcune assegnazioni per l’utente che prevede di deallocare tramite un handle, è spesso ansible avvolgere il quadratino in un puntatore intelligente con un deleter personalizzato che disalloca l’object in modo appropriato.

Ci sono molti casi d’uso per i puntatori.

Comportamento polimorfico . Per i tipi polimorfici, vengono utilizzati puntatori (o riferimenti) per evitare l’affettamento:

 class Base { ... }; class Derived : public Base { ... }; void fun(Base b) { ... } void gun(Base* b) { ... } void hun(Base& b) { ... } Derived d; fun(d); // oops, all Derived parts silently "sliced" off gun(&d); // OK, a Derived object IS-A Base object hun(d); // also OK, reference also doesn't slice 

Semantica di riferimento ed evitare la copia . Per i tipi non polimorfici, un puntatore (o un riferimento) eviterà la copia di un object potenzialmente costoso

 Base b; fun(b); // copies b, potentially expensive gun(&b); // takes a pointer to b, no copying hun(b); // regular syntax, behaves as a pointer 

Si noti che C ++ 11 ha una semantica di spostamento che può evitare molte copie di oggetti costosi nell’argomento di funzione e come valori di ritorno. Ma l’uso di un puntatore eviterà sicuramente quelli e consentirà più puntatori sullo stesso object (mentre un object può essere spostato solo da una volta).

Acquisizione di risorse La creazione di un puntatore a una risorsa utilizzando il new operatore è un anti-pattern nel moderno C ++. Utilizzare una class di risorse speciale (uno dei contenitori Standard) o un puntatore intelligente ( std::unique_ptr<> o std::shared_ptr<> ). Prendere in considerazione:

 { auto b = new Base; ... // oops, if an exception is thrown, destructor not called! delete b; } 

vs.

 { auto b = std::make_unique(); ... // OK, now exception safe } 

Un puntatore raw dovrebbe essere usato solo come “vista” e non in alcun modo coinvolto nella proprietà, sia attraverso la creazione diretta o implicitamente attraverso i valori di ritorno. Vedi anche questo Q & A dalle domande frequenti su C ++ .

Controllo a vita più fine a grana fine Ogni volta che un puntatore condiviso viene copiato (ad es. Come argomento di una funzione) la risorsa a cui punta è mantenuta in vita. Gli oggetti regolari (non creati da new , direttamente da te o all’interno di una class di risorse) vengono distrutti quando si esce dall’ambito.

Ci sono molte risposte eccellenti a questa domanda, compresi gli importanti casi d’uso delle dichiarazioni avanzate, il polimorfismo ecc. Ma sento che una parte dell ‘”anima” della tua domanda non viene esaudita, vale a dire cosa significano le diverse syntax tra Java e C ++.

Esaminiamo la situazione confrontando le due lingue:

Giava:

 Object object1 = new Object(); //A new object is allocated by Java Object object2 = new Object(); //Another new object is allocated by Java object1 = object2; //object1 now points to the object originally allocated for object2 //The object originally allocated for object1 is now "dead" - nothing points to it, so it //will be reclaimed by the Garbage Collector. //If either object1 or object2 is changed, the change will be reflected to the other 

L’equivalente più vicino a questo è:

C ++:

 Object * object1 = new Object(); //A new object is allocated on the heap Object * object2 = new Object(); //Another new object is allocated on the heap delete object1; //Since C++ does not have a garbage collector, if we don't do that, the next line would //cause a "memory leak", ie a piece of claimed memory that the app cannot use //and that we have no way to reclaim... object1 = object2; //Same as Java, object1 points to object2. 

Vediamo il modo alternativo C ++:

 Object object1; //A new object is allocated on the STACK Object object2; //Another new object is allocated on the STACK object1 = object2;//!!!! This is different! The CONTENTS of object2 are COPIED onto object1, //using the "copy assignment operator", the definition of operator =. //But, the two objects are still different. Change one, the other remains unchanged. //Also, the objects get automatically destroyed once the function returns... 

Il modo migliore per pensarlo è che – più o meno – Java (implicitamente) gestisce i puntatori agli oggetti, mentre C ++ può gestire i puntatori agli oggetti o gli oggetti stessi. Ci sono eccezioni a questo – per esempio, se si dichiarano i tipi “primitivi” di Java, sono valori reali che vengono copiati e non puntatori. Così,

Giava:

 int object1; //An integer is allocated on the stack. int object2; //Another integer is allocated on the stack. object1 = object2; //The value of object2 is copied to object1. 

Detto questo, usare i puntatori NON è necessariamente il modo corretto o sbagliato di gestire le cose; tuttavia altre risposte lo hanno soddisfatto in modo soddisfacente. L’idea generale è che in C ++ hai molto più controllo sulla durata degli oggetti e su dove vivranno.

Porta punto di riferimento: il costrutto Object * object = new Object() è in realtà ciò che è più vicino alla semantica tipica di Java (o C # per quella materia).

Un altro buon motivo per usare i puntatori sarebbe per le dichiarazioni anticipate . In un progetto abbastanza grande possono davvero accelerare il tempo di compilazione.

Prefazione

Java non è come il C ++, contrariamente a hype. La macchina hype di Java vorrebbe farvi credere che, poiché Java ha una syntax simile a C ++, le lingue sono simili. Niente può essere più lontano dalla verità. Questa disinformazione è parte del motivo per cui i programmatori Java vanno in C ++ e usano la syntax simile a Java senza comprendere le implicazioni del loro codice.

Avanti andiamo

Ma non riesco a capire perché dovremmo farlo in questo modo. Suppongo che abbia a che fare con efficienza e velocità poiché otteniamo l’accesso diretto all’indirizzo di memoria. Ho ragione?

Al contrario, in realtà. L’heap è molto più lento dello stack, perché lo stack è molto semplice rispetto all’heap. Le variabili di archiviazione automatica (ovvero le variabili di stack) hanno i loro distruttori chiamati quando escono dall’ambito di applicazione. Per esempio:

 { std::string s; } // s is destroyed here 

D’altra parte, se si utilizza un puntatore assegnato dynamicmente, il suo distruttore deve essere chiamato manualmente. delete chiama questo distruttore per te.

 { std::string* s = new std::string; } delete s; // destructor called 

Questo non ha nulla a che fare con la new syntax prevalente in C # e Java. Sono usati per scopi completamente diversi.

Benefici dell’allocazione dynamic

1. Non è necessario conoscere in anticipo la dimensione dell’array

Uno dei primi problemi in cui si trovano molti programmatori C ++ è che quando accettano input arbitrari dagli utenti, è ansible allocare solo una dimensione fissa per una variabile stack. Non è ansible modificare la dimensione degli array. Per esempio:

 char buffer[100]; std::cin >> buffer; // bad input = buffer overflow 

Ovviamente, se hai usato una std::string , invece, std::string ridimensiona internamente in modo che non dovrebbe essere un problema. Ma essenzialmente la soluzione a questo problema è l’allocazione dynamic. È ansible allocare memoria dynamic in base all’input dell’utente, ad esempio:

 int * pointer; std::cout << "How many items do you need?"; std::cin >> n; pointer = new int[n]; 

Nota a margine : un errore che molti principianti fanno è l’uso di array di lunghezza variabile. Questa è un’estensione GNU e anche una in Clang perché rispecchiano molte estensioni di GCC. Quindi il seguente int arr[n] non dovrebbe essere invocato.

Poiché l’heap è molto più grande dello stack, è ansible allocare / riallocare arbitrariamente la quantità di memoria di cui ha bisogno, mentre lo stack ha una limitazione.

2. Gli array non sono puntatori

Come è questo un beneficio che chiedi? La risposta diventerà chiara una volta compresa la confusione / mito dietro array e puntatori. Si presume comunemente che siano uguali, ma non lo sono. Questo mito deriva dal fatto che i puntatori possono essere abbonati come gli array e, a causa degli array, decadono a puntatori al livello più alto in una dichiarazione di funzione. Tuttavia, una volta che un array decade a un puntatore, il puntatore perde la dimensione delle informazioni. Quindi sizeof(pointer) darà la dimensione del puntatore in byte, che di solito è 8 byte su un sistema a 64 bit.

Non è ansible assegnare agli array, solo inizializzarli. Per esempio:

 int arr[5] = {1, 2, 3, 4, 5}; // initialization int arr[] = {1, 2, 3, 4, 5}; // The standard dictates that the size of the array // be given by the amount of members in the initializer arr = { 1, 2, 3, 4, 5 }; // ERROR 

D’altra parte, puoi fare tutto ciò che vuoi con i puntatori. Sfortunatamente, poiché la distinzione tra puntatori e array viene eseguita a mano in Java e C #, i principianti non capiscono la differenza.

3. Polimorfismo

Java e C # dispongono di funzionalità che consentono di trattare gli oggetti come un altro, ad esempio utilizzando la parola chiave as . Quindi se qualcuno volesse trattare un object Entity come un object Player , si potrebbe fare Player player = Entity as Player; Questo è molto utile se intendi chiamare le funzioni su un contenitore omogeneo che dovrebbe applicarsi solo a un tipo specifico. Di seguito è ansible ottenere la funzionalità in un modo simile:

 std::vector vector; vector.push_back(&square); vector.push_back(&triangle); for (auto& e : vector) { auto test = dynamic_cast(e); // I only care about triangles if (!test) // not a triangle e.GenericFunction(); else e.TriangleOnlyMagic(); } 

Quindi, diciamo che se solo Triangoli avesse una funzione Ruota, sarebbe un errore del compilatore se si provasse a chiamarlo su tutti gli oggetti della class. Usando dynamic_cast , puoi simulare la parola chiave as . Per essere chiari, se un cast fallisce, restituisce un puntatore non valido. Quindi !test è essenzialmente una scorciatoia per verificare se il test è NULL o un puntatore non valido, il che significa che il cast non è riuscito.

Benefici delle variabili automatiche

Dopo aver visto tutte le grandi cose che l’allocazione dynamic può fare, probabilmente ti starai chiedendo perché mai nessuno dovrebbe usare l’allocazione dynamic tutto il tempo? Ti ho già detto una ragione, l’heap è lento. E se non hai bisogno di tutta quella memoria, non dovresti abusarne. Quindi qui ci sono alcuni svantaggi in nessun ordine particolare:

  • È sobject a errori. L’allocazione manuale della memoria è pericolosa e si è soggetti a perdite. Se non sei abile nell’usare il debugger o valgrind (uno strumento di perdita di memoria), potresti strapparti i capelli dalla testa. Fortunatamente gli idiomi RAII e i puntatori intelligenti li alleggeriscono un po ‘, ma è necessario avere familiarità con pratiche come The Rule Of Three e The Rule Of Five. Ci sono molte informazioni da raccogliere e i principianti che non sanno o non si preoccupano cadrà in questa trappola.

  • Non è necessario. A differenza di Java e C #, dove è idiomatico usare la new parola chiave ovunque, in C ++, dovresti usarlo solo se necessario. La frase comune va, tutto sembra un chiodo se hai un martello. Mentre i principianti che iniziano con C ++ hanno paura dei puntatori e imparano a usare le variabili di stack per abitudine, i programmatori di Java e C # iniziano usando i puntatori senza comprenderlo! Questo sta letteralmente calpestando il piede sbagliato. Devi abbandonare tutto ciò che conosci perché la syntax è una cosa, imparare la lingua è un’altra.

1. (N) RVO – Aka, (Named) Ottimizzazione del valore di ritorno

Un’ottimizzazione che fanno molti compilatori sono cose chiamate elision e ottimizzazione del valore di ritorno . Queste cose possono ovviare a copys non necessari che sono utili per oggetti molto grandi, come un vettore che contiene molti elementi. Normalmente la pratica comune è usare i puntatori per trasferire la proprietà piuttosto che copiare gli oggetti di grandi dimensioni per spostarli . Questo ha portato alla nascita della semantica del movimento e dei puntatori intelligenti .

Se si utilizzano puntatori, (N) RVO NON si verifica. È più vantaggioso e meno incline agli errori trarre vantaggio da (N) RVO piuttosto che ritornare o passare i puntatori se si è preoccupati dell’ottimizzazione. Le perdite di errore possono verificarsi se il chiamante di una funzione è responsabile dell’eliminazione di un object assegnato dynamicmente e così via. Può essere difficile tracciare la proprietà di un object se i puntatori vengono passati in giro come una patata bollente. Basta usare le variabili dello stack perché è più semplice e migliore.

C ++ ti offre tre modi per passare un object: per puntatore, per riferimento e per valore. Java ti limita con quest’ultimo (l’unica eccezione è rappresentata da tipi primitivi come int, boolean ecc.). Se vuoi usare il C ++ non proprio come un giocattolo strano, allora è meglio conoscere la differenza tra questi tre modi.

Java fa finta che non ci siano problemi come “chi e quando dovrebbe distruggere questo?”. La risposta è: The Garbage Collector, Great and Awful. Tuttavia, non può fornire una protezione al 100% da perdite di memoria (sì, java può perdere memoria ). In realtà, GC ti dà un falso senso di sicurezza. Più grande è il tuo SUV, più lunga è la tua strada verso l’evacuatore.

C ++ ti lascia faccia a faccia con la gestione del ciclo di vita dell’object. Bene, ci sono dei mezzi per gestire questa (famiglia di puntatori intelligenti , QObject in Qt e così via), ma nessuno di questi può essere usato in modalità ‘fire and forget’ come GC: si dovrebbe sempre tenere a mente la gestione della memoria. Non solo ti dovrebbe interessare distruggere un object, devi anche evitare di distruggere lo stesso object più di una volta.

Non ancora spaventato? Ok: riferimenti ciclici – gestiscili da soli, umano. E ricorda: uccidi ogni object con precisione una volta, noi runtime del C ++ non ti piacciono quelli che fanno casino con i cadaveri, lasciano i morti da soli.

Quindi, torna alla tua domanda.

Quando passi il tuo object per valore, non per puntatore o per riferimento, copi l’object (l’intero object, che si tratti di un paio di byte o un enorme dump del database): sei abbastanza intelligente da preoccuparti di evitare quest’ultimo, aren ‘ tu?) ogni volta che fai ‘=’. E per accedere ai membri dell’object, usi ‘.’ (punto).

Quando passi il tuo object per puntatore, copi solo pochi byte (4 su sistemi a 32 bit, 8 su quelli a 64 bit), cioè – l’indirizzo di questo object. E per mostrarlo a tutti, usi questo fantasioso operatore “->” quando accedi ai membri. Oppure puoi usare la combinazione di ‘*’ e ‘.’.

Quando usi i riferimenti, ottieni il puntatore che finge di essere un valore. È un puntatore, ma accedi ai membri tramite ‘.’.

E, per farti saltare in aria ancora una volta: quando dichiari diverse variabili separate da virgole, allora (guarda le mani):

  • Il tipo è dato a tutti
  • Il modificatore di valore / puntatore / riferimento è individuale

Esempio:

 struct MyStruct { int* someIntPointer, someInt; //here comes the surprise MyStruct *somePointer; MyStruct &someReference; }; MyStruct s1; //we allocated an object on stack, not in heap s1.someInt = 1; //someInt is of type 'int', not 'int*' - value/pointer modifier is individual s1.someIntPointer = &s1.someInt; *s1.someIntPointer = 2; //now s1.someInt has value '2' s1.somePointer = &s1; s1.someReference = s1; //note there is no '&' operator: reference tries to look like value s1.somePointer->someInt = 3; //now s1.someInt has value '3' *(s1.somePointer).someInt = 3; //same as above line *s1.somePointer->someIntPointer = 4; //now s1.someInt has value '4' s1.someReference.someInt = 5; //now s1.someInt has value '5' //although someReference is not value, it's members are accessed through '.' MyStruct s2 = s1; //'NO WAY' the compiler will say. Go define your '=' operator and come back. //OK, assume we have '=' defined in MyStruct s2.someInt = 0; //s2.someInt == 0, but s1.someInt is still 5 - it's two completely different objects, not the references to the same one 

In C ++, gli oggetti allocati nello stack (utilizzando Object object; istruzione all’interno di un blocco) vivranno solo all’interno dell’ambito in cui sono dichiarati. Quando il blocco di codice termina l’esecuzione, l’object dichiarato viene distrutto. Mentre se si assegna memoria all’heap, usando Object* obj = new Object() , continuano a vivere nell’heap fino a quando non si chiama delete obj .

Vorrei creare un object su heap quando mi piace usare l’object non solo nel blocco di codice che lo ha dichiarato / allocato.

Ma non riesco a capire perché dovremmo usarlo in questo modo?

Parlerò come funziona all’interno del corpo della funzione se usi:

 Object myObject; 

All’interno della funzione, myObject verrà distrutto una volta restituita questa funzione. Quindi questo è utile se non hai bisogno del tuo object al di fuori della tua funzione. Questo object verrà inserito nella pila di thread corrente.

Se scrivi all’interno del corpo della funzione:

  Object *myObject = new Object; 

quindi l’istanza della class Object puntata da myObject non verrà distrutta al termine della funzione e l’allocazione è nell’heap.

Ora, se sei un programmatore Java, il secondo esempio è più vicino a come funziona l’allocazione degli oggetti sotto java. Questa riga: Object *myObject = new Object; è equivalente a java: Object myObject = new Object(); . La differenza è che sotto java myObject otterrà garbage collection, mentre in c ++ non verrà liberato, dovresti chiamare esplicitamente `delete myObject; ‘ altrimenti si introdurranno perdite di memoria.

Dal momento che c ++ 11 è ansible utilizzare metodi sicuri di allocazioni dinamiche: new Object , memorizzando i valori in shared_ptr / unique_ptr.

 std::shared_ptr safe_str = make_shared("make_shared"); // since c++14 std::unique_ptr safe_str = make_unique("make_shared"); 

inoltre, gli oggetti sono molto spesso memorizzati in contenitori, come map-s o vector-s, che gestiranno automaticamente una vita dei tuoi oggetti.

Tecnicamente si tratta di un problema di allocazione della memoria, tuttavia qui ci sono altri due aspetti pratici di questo. Ha a che fare con due cose: 1) Scope, quando definisci un object senza un puntatore non sarai più in grado di accedervi dopo il blocco di codice in cui è definito, mentre se definisci un puntatore con “nuovo”, allora can access it from anywhere you have a pointer to this memory until you call “delete” on the same pointer. 2) If you want to pass arguments to a function you want to pass a pointer or a reference in order to be more efficient. When you pass an Object then the object is copied, if this is an object that uses a lot of memory this might be CPU consuming (eg you copy a vector full of data). When you pass a pointer all you pass is one int (depending of implementation but most of them are one int).

Other than that you need to understand that “new” allocates memory on the heap that needs to be freed at some point. When you don’t have to use “new” I suggest you use a regular object definition “on the stack”.

Well the main question is Why should I use a pointer rather than the object itself? And my answer, you should (almost) never use pointer instead of object, because C++ has references , it is safer then pointers and guarantees the same performance as pointers.

Another thing you mentioned in your question:

 Object *myObject = new Object; 

Come funziona? It creates pointer of Object type, allocates memory to fit one object and calls default constructor, sounds good, right? But actually it isn’t so good, if you dynamically allocated memory (used keyword new ), you also have to free memory manually, that means in code you should have:

 delete myObject; 

This calls destructor and frees memory, looks easy, however in big projects may be difficult to detect if one thread freed memory or not, but for that purpose you can try shared pointers , these slightly decreases performance, but it is much easier to work with them.


And now some introduction is over and go back to question.

You can use pointers instead of objects to get better performance while transferring data between function.

Take a look, you have std::string (it is also object) and it contains really much data, for example big XML, now you need to parse it, but for that you have function void foo(...) which can be declarated in different ways:

  1. void foo(std::string xml); In this case you will copy all data from your variable to function stack, it takes some time, so your performance will be low.
  2. void foo(std::string* xml); In this case you will pass pointer to object, same speed as passing size_t variable, however this declaration has error prone, because you can pass NULL pointer or invalid pointer. Pointers usually used in C because it doesn’t have references.
  3. void foo(std::string& xml); Here you pass reference, basically it is the same as passing pointer, but compiler does some stuff and you cannot pass invalid reference (actually it is possible to create situation with invalid reference, but it is tricking compiler).
  4. void foo(const std::string* xml); Here is the same as second, just pointer value cannot be changed.
  5. void foo(const std::string& xml); Here is the same as third, but object value cannot be changed.

What more I want to mention, you can use these 5 ways to pass data no matter which allocation way you have chosen (with new or regular ).


Another thing to mention, when you create object in regular way, you allocate memory in stack, but while you create it with new you allocate heap. It is much faster to allocate stack, but it is kind a small for really big arrays of data, so if you need big object you should use heap, because you may get stack overflow, but usually this issue is solved using STL containers and remember std::string is also container, some guys forgot it 🙂

Let’s say that you have class A that contain class B When you want to call some function of class B outside class A you will simply obtain a pointer to this class and you can do whatever you want and it will also change context of class B in your class A

But be careful with dynamic object

There are many benefits of using pointers to object –

  1. Efficiency (as you already pointed out). Passing objects to functions mean creating new copies of object.
  2. Working with objects from third party libraries. If your object belongs to a third party code and the authors intend the usage of their objects through pointers only (no copy constructors etc) the only way you can pass around this object is using pointers. Passing by value may cause issues. (Deep copy / shallow copy issues).
  3. if the object owns a resource and you want that the ownership should not be sahred with other objects.

This is has been discussed at length, but in Java everything is a pointer. It makes no distinction between stack and heap allocations (all objects are allocated on the heap), so you don’t realize you’re using pointers. In C++, you can mix the two, depending on your memory requirements. Performance and memory usage is more deterministic in C++ (duh).

 Object *myObject = new Object; 

Doing this will create a reference to an Object (on the heap) which has to be deleted explicitly to avoid memory leak .

 Object myObject; 

Doing this will create an object(myObject) of the automatic type (on the stack) that will be automatically deleted when the object(myObject) goes out of scope.

A pointer directly references the memory location of an object. Java has nothing like this. Java has references that reference the location of object through hash tables. You cannot do anything like pointer arithmetic in Java with these references.

To answer your question, it’s just your preference. I prefer using the Java-like syntax.

You shouldn’t . People (many people, sadly) write it out of ignorance.

Sometimes dynamic allocation has its place but, in the examples you give, it is wrong .

If you want to think about efficiency, then this is worse , because it introduces indirection for no good reason. This sort of programming is slower and more error-prone .

With pointers ,

  • can directly talk to the memory.

  • can prevent lot of memory leaks of a program by manipulating pointers.

One reason for using pointers is to interface with C functions. Another reason is to save memory; for example: instead of passing an object which contains a lot of data and has a processor-intensive copy-constructor to a function, just pass a pointer to the object, saving memory and speed especially if you’re in a loop, however a reference would be better in that case, unless you’re using an C-style array.

In areas where memory utilization is at its premium , pointers comes handy. For example consider a minimax algorithm, where thousands of nodes will be generated using recursive routine, and later use them to evaluate the next best move in game, ability to deallocate or reset (as in smart pointers) significantly reduces memory consumption. Whereas the non-pointer variable continues to occupy space till it’s recursive call returns a value.

I will include one important use case of pointer. When you are storing some object in the base class, but it could be polymorphic.

 Class Base1 { }; Class Derived1 : public Base1 { }; Class Base2 { Base *bObj; virtual void createMemerObects() = 0; }; Class Derived2 { virtual void createMemerObects() { bObj = new Derived1(); } }; 

So in this case you can’t declare bObj as an direct object, you have to have pointer.

“Necessity is the mother of invention.” The most of important difference that I would like to point out is the outcome of my own experience of coding. Sometimes you need to pass objects to functions. In that case, if your object is of a very big class then passing it as an object will copy its state (which you might not want ..AND CAN BE BIG OVERHEAD) thus resulting in an overhead of copying object .while pointer is fixed 4-byte size (assuming 32 bit). Other reasons are already mentioned above…

There are many excellent answers already, but let me give you one example:

I have an simple Item class:

  class Item { public: std::string name; int weight; int price; }; 

I make a vector to hold a bunch of them.

std::vector inventory;

I create one million Item objects, and push them back onto the vector. I sort the vector by name, and then do a simple iterative binary search for a particular item name. I test the program, and it takes over 8 minutes to finish executing. Then I change my inventory vector like so:

std::vector inventory;

…and create my million Item objects via new. The ONLY changes I make to my code are to use the pointers to Items, excepting a loop I add for memory cleanup at the end. That program runs in under 40 seconds, or better than a 10x speed increase. EDIT: The code is at http://pastebin.com/DK24SPeW With compiler optimizations it shows only a 3.4x increase on the machine I just tested it on, which is still considerable.