übergeben - zeiger c++




Warum sollte ich einen Zeiger anstelle des Objekts selbst verwenden? (15)

Aber ich kann nicht herausfinden, warum sollten wir es so benutzen?

Ich werde vergleichen, wie es im Funktionskörper funktioniert, wenn Sie Folgendes verwenden:

Object myObject;

Innerhalb der Funktion wird Ihr myObject zerstört, sobald diese Funktion zurückkehrt. Dies ist nützlich, wenn Sie Ihr Objekt nicht außerhalb Ihrer Funktion benötigen. Dieses Objekt wird auf den aktuellen Thread-Stack gesetzt.

Wenn Sie innerhalb des Funktionskörpers schreiben:

 Object *myObject = new Object;

Dann wird die Object-Klasseninstanz, auf die myObject zeigt, nicht zerstört, sobald die Funktion endet und die Zuweisung auf dem Heap erfolgt.

Nun, wenn Sie Java-Programmierer sind, dann ist das zweite Beispiel näher, wie die Objektzuordnung unter Java funktioniert. Diese Zeile: Object *myObject = new Object; entspricht java: Object myObject = new Object(); . Der Unterschied ist, dass unter Java myObject Garbage gesammelt wird, während unter C ++ es nicht freigegeben wird, müssen Sie irgendwo `delete myObject explizit aufrufen; ' Andernfalls werden Speicherlecks eingeführt.

Seit c ++ 11 können Sie sichere Wege für dynamische Zuweisungen verwenden: new Object , indem Sie Werte in shared_ptr / unique_ptr speichern.

std::shared_ptr<std::string> safe_str = make_shared<std::string>("make_shared");

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

Außerdem werden Objekte sehr oft in Containern wie map-s oder vector-s gespeichert, sie verwalten automatisch die Lebensdauer Ihrer Objekte.

Ich komme aus einem Java-Hintergrund und habe angefangen, mit Objekten in C ++ zu arbeiten. Aber eine Sache, die mir auffiel, ist, dass Leute oft Zeiger auf Objekte anstatt auf die Objekte selbst verwenden, zum Beispiel diese Deklaration:

Object *myObject = new Object;

eher, als:

Object myObject;

Oder, anstatt eine Funktion zu verwenden, sagen wir testFunc() , so:

myObject.testFunc();

wir müssen schreiben:

myObject->testFunc();

Aber ich kann nicht herausfinden, warum wir es so machen sollten. Ich würde annehmen, dass es mit Effizienz und Geschwindigkeit zu tun hat, da wir direkten Zugriff auf die Speicheradresse haben. Habe ich recht?


Vorwort

Java ist nichts gegen C ++, im Gegensatz zum Hype. Die Java-Hype-Maschine möchte, dass Sie glauben, dass, weil Java C ++ ähnliche Syntax hat, die Sprachen ähnlich sind. Nichts kann weiter von der Wahrheit entfernt sein. Diese Fehlinformation ist einer der Gründe, warum Java-Programmierer nach C ++ gehen und Java-ähnliche Syntax verwenden, ohne die Implikationen ihres Codes zu verstehen.

Weiter gehen wir

Aber ich kann nicht herausfinden, warum wir es so machen sollten. Ich würde annehmen, dass es mit Effizienz und Geschwindigkeit zu tun hat, da wir direkten Zugriff auf die Speicheradresse haben. Habe ich recht?

Im Gegenteil, eigentlich. Der Heap ist viel langsamer als der Stack, da der Stack im Vergleich zum Heap sehr einfach ist. Bei automatischen Speichervariablen (auch Stack-Variablen genannt) werden ihre Destruktoren aufgerufen, sobald sie den Gültigkeitsbereich verlassen haben. Beispielsweise:

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

Wenn Sie jedoch einen dynamisch zugewiesenen Zeiger verwenden, muss sein Destruktor manuell aufgerufen werden. delete ruft diesen Destruktor für dich auf.

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

Dies hat nichts mit der new Syntax in C # und Java zu tun. Sie werden für ganz andere Zwecke verwendet.

Vorteile der dynamischen Zuordnung

1. Sie müssen die Größe des Arrays nicht im Voraus kennen

Eines der ersten Probleme, mit denen viele C ++ - Programmierer konfrontiert sind, besteht darin, dass sie, wenn sie willkürliche Eingaben von Benutzern akzeptieren, nur eine feste Größe für eine Stapelvariable zuweisen können. Sie können die Größe von Arrays auch nicht ändern. Beispielsweise:

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

Natürlich, wenn Sie stattdessen eine std::string verwenden, ändert sich std::string intern selbst, so dass dies kein Problem sein sollte. Aber im Grunde ist die Lösung dieses Problems die dynamische Zuweisung. Sie können dynamischen Speicher basierend auf der Eingabe des Benutzers zuweisen, zum Beispiel:

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

Randnotiz : Ein Fehler, den viele Anfänger machen, ist die Verwendung von Arrays variabler Länge. Dies ist eine GNU-Erweiterung und auch eine in Clang, da sie viele Erweiterungen von GCC widerspiegelt. Daher sollte man sich nicht auf den folgenden int arr[n] verlassen.

Da der Heap viel größer als der Stack ist, kann beliebig viel Speicher zugewiesen / zugewiesen werden, während der Stack eine Beschränkung hat.

2. Arrays sind keine Zeiger

Wie ist das ein Vorteil, den Sie fragen? Die Antwort wird klar, wenn Sie die Verwirrung / Mythos hinter Arrays und Zeigern verstehen. Es wird allgemein angenommen, dass sie gleich sind, aber nicht. Dieser Mythos rührt von der Tatsache her, dass Zeiger wie Arrays subskribiert werden können und weil Arrays in einer Funktionsdeklaration auf der obersten Ebene zu Zeigern zerfallen. Sobald ein Array jedoch zu einem Zeiger abfällt, verliert der Zeiger seine sizeof Information. So gibt sizeof(pointer) die Größe des Zeigers in Bytes an, was normalerweise 8 Bytes auf einem 64-Bit-System ist.

Sie können Arrays nicht zuweisen, sondern nur initialisieren. Beispielsweise:

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

Auf der anderen Seite können Sie mit Zeigern machen, was Sie wollen. Leider, weil die Unterscheidung zwischen Zeigern und Arrays in Java und C # Hand-waved ist, verstehen Anfänger den Unterschied nicht.

3. Polymorphismus

Java und C # verfügen über Funktionen, mit denen Sie Objekte als weitere Objekte behandeln können, z. B. mit dem Schlüsselwort as . Wenn jemand also ein Entity Objekt als ein Player Objekt behandeln möchte, könnte man Player player = Entity as Player; Dies ist sehr nützlich, wenn Sie Funktionen in einem homogenen Container aufrufen möchten, der nur für einen bestimmten Typ gelten soll. Die Funktionalität kann auf ähnliche Weise wie folgt erreicht werden:

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

Also, wenn nur Dreiecke eine Rotate-Funktion hätten, wäre es ein Compilerfehler, wenn Sie versuchen würden, ihn für alle Objekte der Klasse aufzurufen. Mit dynamic_cast können Sie das Schlüsselwort as simulieren. Um klar zu sein, wenn eine Umwandlung fehlschlägt, wird ein ungültiger Zeiger zurückgegeben. So !test ist im Wesentlichen eine Abkürzung für die Überprüfung, ob der test NULL ist oder ein ungültiger Zeiger, was bedeutet, dass die Umwandlung fehlgeschlagen ist.

Vorteile automatischer Variablen

Nachdem Sie all die großartigen Dinge gesehen haben, die die dynamische Allokation bewirken kann, werden Sie sich wahrscheinlich fragen, warum nicht jemand die dynamische Zuweisung die ganze Zeit benutzen würde? Ich habe dir schon einen Grund gesagt, der Haufen ist langsam. Und wenn Sie nicht diese ganze Erinnerung brauchen, sollten Sie sie nicht missbrauchen. Also hier sind einige Nachteile in keiner bestimmten Reihenfolge:

  • Es ist fehleranfällig. Manuelle Speicherzuweisung ist gefährlich und Sie sind anfällig für Lecks. Wenn Sie den Debugger oder valgrind (ein Speicherleck-Tool) nicht beherrschen, können Sie sich die Haare aus dem Kopf reißen. Glücklicherweise lindern RAII-Idiome und intelligente Zeiger das ein wenig, aber Sie müssen mit Praktiken wie der Regel der Drei und der Regel der Fünf vertraut sein. Es ist eine Menge Informationen, und Anfänger, die es entweder nicht wissen oder nicht, werden in diese Falle geraten.

  • Es ist nicht erforderlich. Im Gegensatz zu Java und C #, wo es idiomatisch ist, das new Schlüsselwort überall zu verwenden, sollten Sie es in C ++ nur bei Bedarf verwenden. Der übliche Satz lautet, alles sieht wie ein Nagel aus, wenn du einen Hammer hast. Während Anfänger, die mit C ++ beginnen, Angst vor Zeigern haben und lernen, Stack-Variablen aus Gewohnheit zu verwenden, beginnen Java und C # -Programmierer mit Zeigern, ohne sie zu verstehen! Das geht buchstäblich auf den falschen Fuß. Sie müssen alles aufgeben, was Sie wissen, denn Syntax ist eine Sache, das Erlernen der Sprache ist eine andere Sache.

1. (N) RVO - Aka, (benannt) Rückgabewert-Optimierung

Eine Optimierung, die viele Compiler machen, sind Dinge namens Elision und Return-Value-Optimierung . Diese Dinge können unnötige Kopien vermeiden, was nützlich für sehr große Objekte ist, wie zum Beispiel einen Vektor, der viele Elemente enthält. Normalerweise ist es üblich, Zeiger zu verwenden, um das Eigentum zu übertragen, anstatt die großen Objekte zu kopieren, um sie zu verschieben . Dies hat zur Einführung von Bewegungssemantik und Smartpointern geführt .

Wenn Sie Zeiger verwenden, tritt (N) RVO nicht auf. Es ist vorteilhafter und weniger fehleranfällig, (N) RVO zu nutzen, als Zeiger zurückzugeben oder weiterzugeben, wenn Sie sich Sorgen um die Optimierung machen. Fehlerlecks können auftreten, wenn der Aufrufer einer Funktion für das delete eines dynamisch zugewiesenen Objekts und dergleichen verantwortlich ist. Es kann schwierig sein, den Besitz eines Objekts zu verfolgen, wenn Zeiger wie eine heiße Kartoffel herumgereicht werden. Verwenden Sie einfach Stack-Variablen, weil es einfacher und besser ist.


Ein weiterer guter Grund, Zeiger zu verwenden, wäre für Vorwärtsdeklarationen . In einem ausreichend großen Projekt können sie die Kompilierzeit wirklich beschleunigen.


Es gibt viele Anwendungsfälle für Zeiger.

Polymorphes Verhalten . Bei polymorphen Typen werden Zeiger (oder Referenzen) verwendet, um das Schneiden zu vermeiden:

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

Referenzsemantik und Vermeiden von Kopieren . Bei nicht-polymorphen Typen vermeidet ein Zeiger (oder eine Referenz) das Kopieren eines potenziell teuren Objekts

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

Beachten Sie, dass C ++ 11 eine Bewegungssemantik hat, die viele Kopien teurer Objekte in Funktionsargumente und als Rückgabewerte vermeiden kann. Die Verwendung eines Zeigers wird diese jedoch definitiv vermeiden und mehrere Zeiger auf dasselbe Objekt zulassen (während ein Objekt nur einmal verschoben werden kann).

Ressourcenbeschaffung Einen Zeiger auf eine Ressource mit dem new Operator zu erstellen, ist ein Anti-Pattern im modernen C ++. Verwenden Sie eine spezielle Ressourcenklasse (einen der Standardcontainer) oder einen Smartpointer ( std::unique_ptr<> oder std::shared_ptr<> ). Erwägen:

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

gegen

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

Ein roher Pointer sollte nur als "View" verwendet werden und nicht in irgendeiner Weise in den Besitz einbezogen werden, sei es durch direkte Erstellung oder implizit durch Rückgabewerte. Siehe auch dieses Q & A aus der C ++ - FAQ .

Feinere Lebenszeitsteuerung Jedes Mal, wenn ein gemeinsamer Zeiger kopiert wird (z. B. als Funktionsargument), wird die Ressource, auf die er zeigt, am Leben erhalten. Reguläre Objekte (die nicht von Ihnen selbst oder innerhalb einer Ressourcenklasse erstellt wurden) werden beim Verlassen des Bereichs zerstört.


Es ist sehr bedauerlich, dass Sie die dynamische Zuweisung so oft sehen. Das zeigt nur, wie viele schlechte C ++ - Programmierer es gibt.

In gewissem Sinne haben Sie zwei Fragen zu einem zusammengefasst. Die erste ist, wann sollten wir die dynamische Zuweisung (mit new ) verwenden? Die zweite ist, wann sollten wir Zeiger verwenden?

Die wichtige Botschaft, die Sie mit nach Hause nehmen, ist, dass Sie immer das geeignete Werkzeug für den Job verwenden sollten . In fast allen Situationen gibt es etwas passenderes und sichereres als die manuelle dynamische Zuweisung und / oder die Verwendung roher Zeiger.

Dynamische Zuordnung

In Ihrer Frage haben Sie zwei Möglichkeiten zum Erstellen eines Objekts demonstriert. Der Hauptunterschied ist die Speicherdauer des Objekts. Beim Ausführen von Object myObject; Innerhalb eines Blocks wird das Objekt mit der automatischen Speicherdauer erstellt. Dies bedeutet, dass es automatisch gelöscht wird, wenn es den Gültigkeitsbereich verlässt. Wenn Sie ein new Object() , hat das Objekt eine dynamische Speicherdauer, was bedeutet, dass es so lange am Leben bleibt, bis Sie es explizit delete . Sie sollten die dynamische Speicherdauer nur dann verwenden, wenn Sie sie benötigen. Das heißt, Sie sollten immer Objekte mit automatischer Speicherdauer erstellen, wenn Sie können .

Die zwei wichtigsten Situationen, in denen Sie eine dynamische Zuordnung benötigen:

  1. Sie benötigen das Objekt, um den aktuellen Gültigkeitsbereich zu überstehen - das bestimmte Objekt an diesem bestimmten Speicherort, keine Kopie davon. Wenn Sie mit dem Kopieren / Verschieben des Objekts einverstanden sind (die meiste Zeit sollte es sein), sollten Sie ein automatisches Objekt bevorzugen.
  2. Sie müssen viel Speicher reservieren , was den Stack leicht füllen kann. Es wäre schön, wenn wir uns damit nicht beschäftigen müssten (die meiste Zeit sollte es nicht sein), da es wirklich außerhalb von C ++ liegt, aber leider müssen wir uns mit der Realität der Systeme auseinandersetzen entwickeln sich für.

Wenn Sie die dynamische Zuordnung unbedingt benötigen, sollten Sie sie in einen Smart-Zeiger oder einen anderen Typ RAII , der RAII ausführt (wie die Standardcontainer). Smart Pointer bieten Ownership-Semantiken von dynamisch zugewiesenen Objekten. Schauen Sie sich zum Beispiel std::unique_ptr und std::shared_ptr an. Wenn Sie sie entsprechend verwenden, können Sie fast vollständig vermeiden, eine eigene Speicherverwaltung durchzuführen (siehe die Null-Regel ).

Zeiger

Es gibt jedoch andere allgemeine Verwendungen für rohe Zeiger, die über die dynamische Zuweisung hinausgehen, aber die meisten haben Alternativen, die Sie bevorzugen sollten. Wie zuvor, bevorzugen Sie immer die Alternativen, es sei denn, Sie benötigen wirklich Zeiger .

  1. Sie benötigen Referenzsemantik . Manchmal möchten Sie ein Objekt mithilfe eines Zeigers übergeben (unabhängig davon, wie es zugewiesen wurde), da die Funktion, an die Sie es übergeben, Zugriff auf das betreffende Objekt haben soll (keine Kopie davon). In den meisten Situationen sollten Sie Referenztypen jedoch den Zeigern vorziehen, da dies speziell für sie gedacht ist. Beachten Sie, dass es nicht unbedingt darum geht, die Lebensdauer des Objekts über den aktuellen Bereich hinaus zu verlängern, wie in obiger Situation 1. Wenn Sie eine Kopie des Objekts übergeben möchten, benötigen Sie wie zuvor keine Referenzsemantik.

  2. Sie brauchen Polymorphie . Sie können Funktionen nur polymorph (dh gemäß dem dynamischen Typ eines Objekts) über einen Zeiger oder eine Referenz auf das Objekt aufrufen. Wenn das das Verhalten ist, das Sie benötigen, müssen Sie Zeiger oder Referenzen verwenden. Auch hier sollten Referenzen bevorzugt werden.

  3. Sie möchten darstellen, dass ein Objekt optional ist, indem Sie nullptr , dass ein nullptr übergeben wird, wenn das Objekt ausgelassen wird. Wenn es sich um ein Argument handelt, sollten Sie lieber Standardargumente oder Funktionsüberladungen verwenden. Andernfalls sollten Sie lieber einen Typ verwenden, der dieses Verhalten kapselt, z. B. std::optional (eingeführt in C ++ 17 - mit früheren C ++ - Standards, verwenden Sie boost::optional ).

  4. Sie möchten die Übersetzungseinheiten entkoppeln, um die Übersetzungszeit zu verbessern . Die nützliche Eigenschaft eines Zeigers ist, dass Sie nur eine Vorwärtsdeklaration des angegebenen Typs benötigen (um das Objekt tatsächlich zu verwenden, benötigen Sie eine Definition). Dadurch können Sie Teile Ihres Kompilierungsprozesses entkoppeln, was die Kompilierungszeit erheblich verbessern kann. Siehe das Pimpl-Idiom .

  5. Sie müssen eine Schnittstelle mit einer C-Bibliothek oder einer C-style-Bibliothek herstellen. An diesem Punkt müssen Sie rohe Zeiger verwenden. Das Beste, was Sie tun können, ist sicherzustellen, dass Sie Ihre rohen Zeiger erst im letzten möglichen Moment loslassen. Sie können einen rohen Zeiger von einem intelligenten Zeiger abrufen, z. B. indem Sie die Funktion get member verwenden. Wenn eine Bibliothek eine Zuweisung für Sie vornimmt, von der erwartet wird, dass Sie die Zuordnung über ein Handle aufheben, können Sie das Handle häufig in einem intelligenten Zeiger mit einem benutzerdefinierten Löschelement umbrechen, das das Objekt entsprechend freigibt.


In C ++ werden Objekte, die auf dem Stapel zugeordnet sind (mit Object object; Anweisung innerhalb eines Blocks) nur innerhalb des Gültigkeitsbereichs ausgeführt, in dem sie deklariert sind. Wenn der Codeblock die Ausführung beendet, wird das deklarierte Objekt zerstört. Wenn Sie jedoch mit Hilfe von Object* obj = new Object() Speicher auf dem Heap Object* obj = new Object() , leben sie weiterhin im Heap, bis Sie delete obj .

Ich würde ein Objekt auf Heap erstellen, wenn ich das Objekt nicht nur in dem Block des Codes verwenden möchte, der es deklariert / zugewiesen hat.


"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 overhead of copying object .while pointer is fixed 4 byte size (assuming 32 bit).Other reasons are already mentioned above...


With pointers ,

  • can directly talk to the memory.

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


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.


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.


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.


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).


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;

Wie funktioniert es? 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 , but usually this issue is solved using STL containers and remember std::string is also container, some guys forgot it :)


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.







c++11