sinixnd
Goto Top

(Cpp) Verständnisproblem: Nutzen des new-operators? (mit Beispiel)

Hallo liebe community!

INTRO:
Zunächsteinmal: Trotz mehrerer Stunden Recherche habe ich für meine Frage leider noch keine Antwort gefunden und wende mich daher vertrauensvoll an dieses Forum. Ich war früher schon mal einige Zeit hier unterwegs, habe mich aber damals mit Batch beschäftigt =) Das Forum blieb mir immer in seeehr guter Erinnerung.

Aufgrund meiner in absehbarer Zeit anstehenden Bachelorarbeit im Maschinenbau-Studium, bei der ich ein Programm in C++ schreiben kann/muss/darf/soll (=D), beschäftige ich mich nun seit einigen Tagen relativ intensiv mit C++, mit welcher ich vorher noch nie Kontakt hatte.

Mittlerweile bin ich bei "Dynamischen Strukturen - Anlegen und Freigeben von Speicher" angekommen (aus der von meinem Prof. vorgeschlagenen online-version des Buches "Einstieg in C++" von Willemer ; ich nutze auch andere Quellen wie youtube, google und die cpp-Einführung auf highscore.de, die aber keinen http-link hat, daher möchte ich sie nicht verlinken)

FRAGE:
Mir erschließt sich die Nutzung des new-operators nicht. Warum nutzt man ihn? Welche Vor- oder Nachteile hat er im Vergleich zu anderen Möglichkeiten? Ich komm nicht drauf.

BEISPIEL:
in einem youtube-guide (link) fand ich ein schönes Beispiel, bei dem mein "Problem" hoffentlich klar wird. Das Programm erzeugt eine Matrix (als Vergleich wurde ein Bild genannt), welche dann mit 0-ern gefüllt wird. Als Anzahl für Zeilen (r) und Spalten (c) übernahm ich r=800 und c=600 aus dem video. Beide Varianten funktionieren meines Erachtens einwandfrei. Daher ist mir der Vorteil der ersten nicht ersichtlich.

#include <iostream>
#include <iomanip>
using namespace std; 

int main()
/*
{
    int r, c;
    cout << "Zeilen eingeben: " << endl; 
    cin >> r;
    cout << "Spalten eingeben: " << endl; 
    cin >> c;

    int **matrix = new int*[r];
    for (int i=0; i<r; i++)
    {
        matrix[i] = new int[c];
    }

    for (int i=0; i<r; i++)
    {
        for (int j=0; j<c; j++)
        {
            matrix[i][j] = 0;
        }
    }
    cout << matrix << endl;

     for (int i=0; i<r; i++)
    {
        delete matrix[i];
    }
    delete matrix;
return 0;
}
*/

//An dieser Stelle endet der Code im Video. Ab hier beginnt mein eigener Versuch.

{
    int r, c;
    cout << "Zeilen eingeben: " << endl;  
    cin >> r;
    cout << "Spalten eingeben: " << endl;  
    cin >> c;

    int matrix[r][c];

    for (int i=0; i<r; i++)
    {
        for (int j=0; j<c; j++)
        {
            matrix[i][j] = 0;
        }
    }
    cout << matrix << endl;
}

Beide codes (der auskommentierte original-code sowie mein eigener Versuch) Haben das gleiche Ergebnis.
Daher stellt sich mir die Frage, wozu man den new-operator braucht. Offensichtlich (für mich) kann man auch ohne new-operator während des laufenden Programms Variablen/Matrizen und so deklarieren, die dann auf Nutzereingaben (oder vermutlich auch anderen Input) reagieren und sich so dem Bedarf anpassen.

SCHLUSS
Ich hoffe, dass mein Problem ersichtlich wurde und mir jemand helfen kann =)

Sollte ich irgendetwas vergessen haben, möchte ich mich im Voraus dafür entschuldigen und werde es natürlich baldmöglichst korrigieren. Verzeigt bitte auch, sollte ich manche Begriffe falsch benutzt haben, auf die paar Tage kenne ich natürlich vieles noch nicht und bin mit der Verwendung der Fachbegriffe uU. noch nicht ganz vertraut ;) Sollten mehr Kommentare im Code erwünscht sein, ergänze ich sie gerne.

Vielen Dank schon mal an alle Kommentatoren

SinixND

Content-Key: 616395

Url: https://administrator.de/contentid/616395

Printed on: April 19, 2024 at 20:04 o'clock

Member: mayho33
mayho33 Oct 26, 2020 at 01:05:03 (UTC)
Goto Top
Hi,

Wenn du vor dem Bachelor stehst, solltest du ja schon einige Berührungspunkte haben mit C++. Vorausgesetzt du hast da nicht gefehlt face-wink

Der New() -Operator erzeugt in fast allen Programmiersprachen eine neu Instanz des angegebenen Objekts

Powershell:
$a = New-Object ...

C#:
Object x = New Directoryinfo()....

Dito in cpp usw.

Hier die Referenz zu den Keywords:

https://en.cppreference.com/w/cpp/keyword

Grüße!
Member: SinixND
SinixND Oct 26, 2020 updated at 01:14:43 (UTC)
Goto Top
naja, es ist ein Maschinenbau-Bachelor, wir hatten 1 Semester visual basic, das wars ;)

back to topic:
auch eine Variablen-deklaration erzeugt doch eine neue Instanz, oder? wo ist also der mehrwert ggü. der (viel kürzeren) Variante?

den Link schau ich mir mal an ;)
Member: KLinnebank
KLinnebank Oct 26, 2020 at 07:48:09 (UTC)
Goto Top
Moin zusammen,

kurz und simpel ausgedrückt:

Das erste Beispiel erzeugt und verwendet ein Objekt (naja, eigentlich eine Liste von Objekten)
im dynamisch "allgemeinen Speicher". Der Operateor delete gibt diesen Speicher wieder frei.
Das Objekt kann duch Dein Programm und seine Funktionen durchgereicht werden und darf nur von
Deinem Programm zerstört werden zu einem Zeitpunkt, den Du kontrollierenkannst.

Dein Beispiel erzeugt ein lokales Speicherobjekt, das nur Gültigkeit besitzt
innerhal Deiner lokalen Funktion, es wird automatisch beim Verlassen der Funktion zerstört
und kann nicht (zumindest nicht "nach oben" zurück) weitergegeben werden.

ICh hoffe, ich konnte rüberbringen, was ich meine....

Mfg

KLinnebank
Member: rubberman
rubberman Oct 26, 2020 at 07:52:07 (UTC)
Goto Top
Wenn du new in C++ nutzt, hast du im Zweifelsfall einen Designfehler in deinem Programm. Wenn du new ohne Verwendung eines Smartpointers verwendest, hast du definitiv etwas falsch gemacht. Die Gefahr eine Speicherverletzung zu verursachen ist zu groß, darum baut C++ auf das RAII Prinzip. In deinem Fall würde man eine der STL Containerklassen nutzen. Youtube ist in aller Regel kein guter Anlaufpunkt um Programmieren zu lernen...

Steffen
Member: SinixND
SinixND Oct 26, 2020 at 08:17:00 (UTC)
Goto Top
von dem Vorteil von Smartpointern hab ich schon gehört.
Vorrangig geht es mir ums Verständnis des neu-operators. Schadet bestimmt auch für die Verwendung von Smartpointern nicht.
Youtube kam erst bei der Suche nach einem Eklärungsversuch ;)

Danke trotzdem face-smile
Member: rubberman
rubberman Oct 26, 2020 updated at 09:07:25 (UTC)
Goto Top
Gut, was new macht ist glaube ich hinreichend erklärt. Es stellt dir Speicher vom Stack zur Verfügung, der dann durch delete wieder freigegeben muss. Bei Smartpointern erledigt letzteres der Destruktor.

Anmerkung noch zu deinem eigenen Code - du erzeugst ein sogenanntes Variable Length Array (VLA). Das wird auf dem Heap allokiert. Geht ganz schnell ohne jede Vorwarnung in die Hose wenn du zu viele Elemente allokieren musst und solltest du unter allen Umständen vermeiden!

Steffen
Member: SinixND
SinixND Oct 26, 2020 updated at 09:18:17 (UTC)
Goto Top
Das heißt sozusagen:
Im Gegensatz zu
int matrix[r][c];
kann auf
int** matrix = new int*[r];
    for (int i=0; i<r; i++)
    {
        matrix[i] = new int[c];
    }
überall im Programm (also auch außerhalb der entsprechenden Klammer) zugegriffen werden...
Member: rubberman
rubberman Oct 26, 2020 at 09:59:22 (UTC)
Goto Top
Speicher wird für deinen Prozess allokiert. Der ist also grundsätzlich global gültig. Variablen, die den Speicher ggf. referenzieren, haben aber nur Gültigkeit für das Scope in dem sie deklariert wurden. Da liegt der Unterschied.

Steffen
Member: SinixND
SinixND Oct 26, 2020 updated at 11:00:53 (UTC)
Goto Top
ah, das war in etwa dass, was ich gesucht hab face-smile
wenn ich mein wissen jetzt mal zusammenfasse:

- new ist "persistent" bis "delete", die variable gilt also auch außerhalb (über/unter?) der klammer(scope)
- new weist der erstellten variable/array auch gleich einen Zeiger zu, was sonst in zwei schritten gemacht werden müsste
- new erlaubt (noch nicht im skript definierte) variablen in arrays (entspricht definition zu laufzeit?). dies geht bei einer "normalen" array-deklaration nicht (entspricht definition zu kompilier-zeit?)

bin mir nur bei allen Punkten nich sicher, ob das stimmt…
Member: rubberman
Solution rubberman Oct 26, 2020 at 11:02:17 (UTC)
Goto Top
- new ist "persistent" bis "delete", die variable gilt also auch außerhalb (über/unter?) der klammer
Hmpf. Der Pointer (die Adresse des allokierten Speichers) bleibt gültig. Variablen selber haben aber immer eine definierte Sichtbarkeit (Scope). Du kannst also nicht einfach eine Variable foo in der main deklarieren und ihr einen allokierten Pointer zuweisen, und anschließend davon ausgehen dass du die Variable foo in einer anderen Funktion verwenden kannst. Du kannst einen allokierten Pointer aber aus einer Funktion zurückgeben, ohne Gefahr zu laufen dass er ungültig wird.

- new weist der erstellten variable/array auch gleich einen Zeiger zu, was sonst in zwei schritten gemacht werden müsste
Was meinst du mit "sonst"? Du allokierst so etwas wie ein zweidimensionales Array. Dazu wird ein Array von Pointers auf int allokiert, der die "Zeilen" als Elemente beinhalten. Für jede Zeile muss aber noch mal ein Array von int allokiert werden für die Einzelwerte einer Zeile. Jeder dieser Pointerwerte muss mit delete wieder freigegeben werden. Da reden wir also von wesentlich mehr als nur zwei Schritten.

- new erlaubt (noch nicht im skript definierte) variablen in arrays (entspricht definition zu laufzeit?). dies geht bei einer "normalen" array-deklaration nicht (entspricht definition zu kompilier-zeit?)
Richtig. Wenn die Größe erst zur Laufzeit festgelegt wird, baust du ansonsten ein VLA.

Aber ich muss noch mal klar darauf hinweisen, dass das kein gutes C++ wird. Die STL bietet dir hier std::array oder std::vector für solche Zwecke …

Steffen
Member: SinixND
SinixND Oct 26, 2020 at 12:03:41 (UTC)
Goto Top
vielen Dank für die ausführliche Antwort face-smile
sie hat auf jeden Fall viel geholfen face-smile

zu 1) dh, obwohl die Variable außerhalb des scopes nicht mehr ansprechbar ist, bleibt der zugewiesene pointer ansprechbar?

2) mit "sonst" meinte ich, dass man sonst (1.) bspw. ein array deklarieren muss, auf welches man dann (2.) einen pointer referenziert.
ich wüsste beispielsweise gar nicht, wie ich "nachträglich" pointer auf die elemente eines "int array[5][5]" referenzieren könnte. falls das mit for-schleifen klappt, ist in der hinsicht der new-operator "nur" kürzer zu schreiben.

3) sehr gut face-smile
Member: rubberman
Solution rubberman Oct 26, 2020 updated at 12:37:07 (UTC)
Goto Top
zu 1) dh, obwohl die Variable außerhalb des scopes nicht mehr ansprechbar ist, bleibt der zugewiesene pointer ansprechbar?
Er bleibt gültig. Aber ohne Variable (die nichts anderes tut als den Pointer/die Adresse zu speichern) hast du keinen Zugriff. Und genau da liegt die Gefahr. Verlierst du die Adresse (indem eine Variable überschrieben oder ungültig wird, oder indem der ursprüngliche Pointerwert durch Pointerarithmetik verändert wurde), dann kann er nicht mehr freigegeben werden und du erzeugst ein Speicherleck.

2) mit "sonst" meinte ich, dass man sonst (1.) bspw. ein array deklarieren muss, auf welches man dann (2.) einen pointer referenziert.
ich wüsste beispielsweise gar nicht, wie ich "nachträglich" pointer auf die elemente eines "int array[5][5]" referenzieren > könnte. falls das mit for-schleifen klappt, ist in der hinsicht der new-operator "nur" kürzer zu schreiben.
Ein Array ist immer gleichzeitig ein Pointer. Im Gegensatz zu einer Arrayvariablen enthält aber eine Pointervariable keinen Hinweis mehr, auf wie viele Elemente verwiesen wir. Man spricht von Array-To-Pointer-Decay.

Ich hatte da vor einiger Zeit mal was dazu runtergeschrieben …
https://dev-community.de/resources/c-pointer.6/

Steffen
Member: SinixND
SinixND Oct 26, 2020 updated at 18:01:47 (UTC)
Goto Top
[OT]

dein link ist sehr interessant. Wenn ich mir (bin jetzt zur hälfte durch) einen Kommentar/Frage/Verbesserungsvorschlag erlauben darf:

mir wurden Pointer in dem oben erwähnten Buch mit folgendem Beispiel zum ersten Mal gezeigt:
char *charZeiger;

Ich als Newbie hab das so gelesen:
char - das ist der pointertyp
*charZeiger - das ist der pointername

und hab daraus gefolgert, dass ein pointer über das " * " am Namensanfang identifiziert und von normalen Variablen unterschieden wird.
Diese Schreibweise habe ich auch in deinem Guide gefunden.

Inzwischen weiß ich aber, dass der Inhalt eines int-Pointers ein anderer ist als der einer int-Variable.

Daher hat der Pointer genaugenommen einen anderen Typ, nämlich int* (wodurch das Programm ganz klar weiß: Jetzt lege ich einen Pointer für eine int-Variable an, und keine int-Variable).
Ich denke deshalb, dass
char* charZeiger;
die verständlichere, intuitivere Schreibweise ist =)

Oder gibt es einen bestimmten Grund, lieber "int *p" anstatt "int* p" zu schreiben?
Member: rubberman
rubberman Oct 26, 2020 at 18:15:52 (UTC)
Goto Top
Darüber scheiden sich die Geister. Erstmal
int *p;
ist dasselbe wie
int * p;
ist dasselbe wie
int* p;

In Punkto intuitiv kannst du auf die Nase fallen. Beispiel:
int* p, x;
Wenn du jetzt denkst, dass x auch ein Pointer ist, liegst du verkehrt, denn x ist ein int. Darum ziehe ich das Asterisk vor den Variablenname:
int *p, *x;
Nun ist klar, auch x ist ein Pointer face-wink Aber wie gesagt, das ist lediglich eine Frage des Stils, den du für dich selbst finden musst, dann aber möglichst konsistent verwenden solltest um den Leser des Codes nicht zu verwirren.

Steffen
Member: SinixND
SinixND Oct 26, 2020 at 18:26:38 (UTC)
Goto Top
ah ich verstehe, das macht dann natürlich Sinn (und ist gleichzeitig etwas doof.. xD)
Member: mayho33
mayho33 Oct 27, 2020 at 09:08:38 (UTC)
Goto Top
Zitat von @SinixND:
auch eine Variablen-deklaration erzeugt doch eine neue Instanz, oder? wo ist also der mehrwert ggü. der (viel kürzeren) Variante?

Wenn du eine Variable deklarierst, dann erstellst du ein neues Object der BASE (z.B. String), das stimmt. der New-Operator erstellt aber eine neue Instalnz des Objects das du schon erstellt hast.

bsp:
class MeineKlasse
{
    ...
}

var nw = new MeineKlasse();

Grüße!
Member: SinixND
SinixND Oct 27, 2020, updated at Oct 28, 2020 at 02:46:33 (UTC)
Goto Top
soweit ich das jetzt verstanden hab erstellt new schon auch neue Variablen, aber der speicher wird nicht automatisch wieder freigegeben, sobald die klammer der erstellten variable verlassen wird. außerdem gibt new die adresse zurück, nicht den Wert.

int main() 
{ 
   {
    int *p = new int(1);
    cout << *p << endl;
    delete p;
    }
//cout << *p << endl;
  
} 
funktioniert einwandfrei. Die Variable hat zwar keinen Namen, aber p zeigt auf die Variable ohne Namen. Allerdings kann man mit der auskommentierten Zeile *p nicht mehr ausgeben, also die erstellte Variable und der Zeiger sind nicht mehr aufrufbar, sobald das scope verlassen wurde. Deshalb verstehe ich die Existenz des new befehls nicht (abgesehen davon, dass man sich die Benennung der Variable spart xD).

Ich weiß inzwischen, dass der new-Befehl sehr unbeliebt ist, weil man viele Fehler machen kann und es wohl sowohl dafür als auch für Pointer bessere Lösungen gibt. Ich Frage nur um des Wissens willen =)
Member: mayho33
mayho33 Oct 29, 2020 at 09:03:17 (UTC)
Goto Top
Zitat von @SinixND:
> 
> int main() 
> { 
>    {
>     int *p = new int(1);
>     cout << *p << endl;
>     delete p;
>     }
> //cout << *p << endl;
>   
> } 
> 

Ich weiß inzwischen, dass der new-Befehl sehr unbeliebt ist, weil man viele Fehler machen kann und es wohl sowohl dafür als auch für Pointer bessere Lösungen gibt. Ich Frage nur um des Wissens willen =)


Ist schon einige Zeit her, da ich mich mit cpp gespielt habe, aber wenn sich nichts geändert hat dann ist das Problem immer noch der Carbagecollector. Der Destruktor der BASE wird nicht automatisch aufgerufen und dein Objekt existiert bis zum Nimmerleinstag. Ergo: irgendwann gibts einen Stackoverflow.

In C# passiert das automatisch. Und da ist der New-Operator bei einigen Dingen ein muss weil Instanzen nur so vererbt werden können. Da gibts allerdings auch keine Pointer, alloc usw. Alles automatisiert.

Grüße!
Member: rubberman
rubberman Oct 29, 2020 at 17:26:36 (UTC)
Goto Top
Carbagecollector
In C++? Nee, da gibt's so etwas nicht. C# unterscheidet sich da grundlegend. Auch hinsichtlich new, das man in C++ nur in Ausnahmefällen verwendet und, wie schon geschrieben, ohne Smart Pointer Klasse baut man mit new gleich mal grundsätzlich einen Designfehler in den Code.

Steffen
Member: mayho33
mayho33 Oct 29, 2020 at 23:49:29 (UTC)
Goto Top
Zitat von @rubberman:

Carbagecollector
In C++? Nee, da gibt's so etwas nicht. C# unterscheidet sich da grundlegend. Auch hinsichtlich new, das man in C++ nur in Ausnahmefällen verwendet und, wie schon geschrieben, ohne Smart Pointer Klasse baut man mit new gleich mal grundsätzlich einen Designfehler in den Code.

Steffen

Wusste ich doch dass es einen Grund hatte warum ich mit cpp aufgehört habe face-smile