Frage

Ich habe folgende Fragen zu Zeichenfolgen in C ++

1 >> Welches ist eine bessere Option (berücksichtigt die Leistung) und warum?

1.

string a;
a = "hello!";

ODER

2.

string *a;
a = new string("hello!");
...
delete(a);

2>>

string a;
a = "less"; 
a = "moreeeeeee"; 

Wie genau wird die Speicherverwaltung in C ++ behandelt, wenn eine größere Zeichenfolge in eine kleinere Zeichenfolge kopiert wird? Sind C ++ - Strings veränderlich?

War es hilfreich?

Lösung

Die folgende Aussagen ist das, was ein naiver Compiler tun würde. Solange es das Verhalten des Programms nicht ändert, ist der Compiler frei, um optimiert zu werden.

string a;
a = "hello!";

Zuerst initialisieren Sie a so, dass Sie die leere Zeichenfolge enthalten. (Länge auf 0 und ein oder zwei andere Operationen einstellen). Dann weisen Sie einen neuen Wert zu und überschreiben den bereits festgelegten Längenwert. Möglicherweise muss auch eine Prüfung durchgeführt werden, um zu sehen, wie groß der aktuelle Puffer ist und ob mehr Speicher zugewiesen werden sollte oder nicht.

string *a;
a = new string("hello!");
...
delete(a);

Das Aufrufen von Neuen erfordert das Betriebssystem und der Speicher Allocator, um einen kostenlosen Speicherblock zu finden. Das ist langsam. Dann initialisieren Sie es sofort, sodass Sie nicht zweimal etwas zuweisen oder den Puffer wie in der ersten Version verlangen. Dann passiert etwas Schlimmes, und Sie vergessen, das Löschen anzurufen, und Sie haben ein Speicherleck. zusätzlich zu einer Saite, die extrem langsam zuordnen kann. Das ist also schlecht.

string a;
a = "less"; 
a = "moreeeeeee";

Wie im ersten Fall initialisieren Sie zuerst A, um die leere Zeichenfolge zu enthalten. Dann weisen Sie eine neue Zeichenfolge und dann eine andere zu. Jedes von diesen kann Erfordern Sie einen Anruf bei Neu, um mehr Speicher zuzuweisen. Jede Zeile erfordert auch Länge und möglicherweise andere interne Variablen zuzuweisen.

Normalerweise würden Sie es so zuweisen:

string a = "hello";

Eine Zeile, die Initialisierung einmal durchführen, anstatt zuerst die Standardeinitialisierung und dann den gewünschten Wert zuweisen.

Es minimiert auch Fehler, da Sie überall in Ihrem Programm keine unsinnige leere Zeichenfolge haben. Wenn die Zeichenfolge existiert, enthält sie den gewünschten Wert.

Über Speicherverwaltung, Google Raii. Kurz gesagt, Zeichenfolge ruft neue/lösche intern an, um die Größe des Puffers zu ändern. Das heißt du noch nie Müssen eine Zeichenfolge mit neuer String zuweisen. Das String -Objekt hat eine feste Größe und ist so konzipiert, dass sie auf dem Stapel zugewiesen werden soll, damit der Destruktor ist automatisch gerufen, wenn es aus dem Spielraum ausgeht. Der Destruktor garantiert dann, dass ein zugewiesenes Gedächtnis befreit wird. Auf diese Weise müssen Sie in Ihrem Benutzercode kein Neues/Löschen verwenden, was bedeutet, dass Sie keinen Speicher auslaufen lassen.

Andere Tipps

Es ist fast nie notwendig oder wünschenswert zu sagen

string * s = new string("hello");

Immerhin würden Sie (fast) nie sagen:

int * i = new int(42);

Sie sollten stattdessen sagen

string s( "hello" );

oder

string s = "hello";

Und ja, C ++ - Strings sind veränderlich.

Gibt es einen bestimmten Grund, warum Sie die Zuordnung anstelle der Intialisierung ständig verwenden? Das heißt, warum schreibst du nicht?

string a = "Hello";

etc.? Dies vermeidet eine Standardkonstruktion und macht semantisch mehr sinnvoller. Das Erstellen eines Zeigers auf eine Zeichenfolge, nur um ihn auf dem Haufen zuzuweisen, ist nie sinnvoll, dh Ihr Fall 2 macht keinen Sinn und ist etwas weniger effizient.

In Bezug auf Ihre letzte Frage sind die Zeichenfolgen in C ++ veränderlich, sofern nicht deklariert const.

string a;
a = "hello!";

2 Vorgänge: Ruft den Standardkonstruktor STD: String () auf und ruft dann den Bediener :: = auf

string *a; a = new string("hello!"); ... delete(a);

Nur eine Operation: Ruft den Konstruktor STD: String (const char*) auf, aber Sie sollten nicht vergessen, Ihren Zeiger zu veröffentlichen.

Was ist mit String A ("Hallo");

In Fall 1.1 Ihre Zeichenfolge Mitglieder (einschließlich Zeiger auf die Daten) werden in gehalten stack und die von der Klasseninstanz besetzte Erinnerung wird befreit, wenn a geht aus dem Spielraum.

In Fall 1.2 wird auch der Speicher für die Mitglieder dynamisch vom Heap zugewiesen.

Wenn Sie a zuweisen char* Konstant zu einem Zeichenfolge, der Speicher, der die Daten enthalten realloc'Ed, um die neuen Daten anzupassen.

Sie können sehen, wie viel Speicher durch Anrufe zugewiesen wird string::capacity().

Wenn du anrufst string a("hello"), Das Speicher wird im Konstruktor zugewiesen.

Sowohl der Konstruktor als auch der Zuordnungsbetreiber rufen die gleichen Methoden intern auf, um dort Speicher zu vergeben und neue Daten zu kopieren.

Wenn Sie sich das ansehen Dokumente Für die STL -String -Klasse (ich glaube, die SGI -Dokumente sind der Spezifikation konform), listen viele der Methodenkomplexitätsgarantien auf. Ich glaube, viele der Komplexitätsgarantien sind absichtlich vage, um unterschiedliche Implementierungen zu ermöglichen. Ich denke, einige Implementierungen verwenden tatsächlich einen Copy-on-Modify-Ansatz, sodass die Zuweisung einer String einer anderen eine konstante Zeitoperation ist. Sie können jedoch unerwartete Kosten anfallen, wenn Sie versuchen, eine dieser Instanzen zu ändern. Ich bin mir nicht sicher, ob das in der modernen STL immer noch der Fall ist.

Sie sollten auch das überprüfen capacity() Funktion, die Ihnen die maximale Längenzeichenfolge mitteilt, die Sie in eine bestimmte String -Instanz einfügen können, bevor sie gezwungen werden, den Speicher zu verwirklichen. Sie können auch verwenden reserve() Um einen bestimmten Betrag neu zuzuordnen, wenn Sie wissen, dass Sie zu einem späteren Zeitpunkt eine große Zeichenfolge in der Variablen speichern.

Wie andere gesagt haben, sollten Sie in Bezug auf Ihre Beispiele die Initialisierung gegenüber anderen Ansätzen wirklich bevorzugen, um die Erstellung von temporären Objekten zu vermeiden.

Das Erstellen einer Zeichenfolge direkt im Haufen ist normalerweise keine gute Idee, genau wie das Erstellen von Basistypen. Es lohnt sich nicht, da das Objekt problemlos auf dem Stapel bleiben kann und alle Kopierkonstruktoren und Zuordnungsbetreiber für eine effiziente Kopie benötigt.

Die STD: String selbst verfügt über einen Puffer im Heap, der je nach Implementierung von mehreren Zeichenfolge geteilt werden kann.

Für IntSance können Sie mit der STL -Implementierung von Microsoft das tun:

string a = "Hello!";
string b = a;

Und beide String würden den gleichen Puffer teilen, bis Sie ihn geändert haben:

a = "Something else!";

Deshalb war es sehr schlecht, den C_str () für die letztere Verwendung zu speichern; c_str () garentee nur Gültigkeit, bis ein anderer Aufruf dieses String -Objekts getätigt wird.

Dies führte zu sehr bösen Parallelitätsfehlern, bei denen diese Freigabefunktionen mit einem definierten ausgeschaltet werden mussten, wenn Sie sie in einer Multithread -Anwendung verwendet haben

Höchstwahrscheinlich

   string a("hello!");

ist schneller als alles andere.

Du kommst aus Java, oder? In C ++ werden Objekte (in den meisten Arten) gleich behandelt wie die Grundwerttypen. Objekte können auf dem Stapel oder im statischen Speicher leben und von Wert übergeben werden. Wenn Sie eine Zeichenfolge in einer Funktion deklarieren, die den Stapel auf dem Stapel zuteilt, so viele Bytes, das das String -Objekt erfordert. Das String -Objekt selbst verwendet dynamisches Speicher, um die tatsächlichen Zeichen zu speichern, aber das ist für Sie transparent. Die andere Sache, an die Sie sich erinnern sollten, ist, dass beim Ausgang der Funktion und die von Ihnen deklarierte Zeichenfolge nicht mehr im Rahmen des von ihr verwendeten Speichers befreit werden. Keine Notwendigkeit für die Müllsammlung (Raii ist Ihr bester Freund).

In Ihrem Beispiel:

string a;
a = "less"; 
a = "moreeeeeee";

Dies setzt einen Speicherblock auf den Stapel und nennt ihn a, dann wird der Konstruktor aufgerufen und A in eine leere Zeichenfolge initialisiert. Der Compiler speichert die Bytes für "weniger" und "moreeeeeee" in (ich glaube) den .rdata -Abschnitt Ihres Exe. String A wird ein paar Felder haben, wie ein Längenfeld und ein Char* (ich vereinfache stark). Wenn Sie A "weniger" zuweisen, wird die Methode operator = () aufgerufen. Es verteilt den Speicher dynamisch zum Speichern des Eingangswert in den internen Puffer.

Wenn der Bereich von String A beendet ist, wird der String -Destruktor aufgerufen und der Speicher, der dynamisch zugewiesen wurde, um die tatsächlichen Zeichen zu halten, wird befreit. Dann wird der Stapelzeiger abgeschlossen und der Gedächtnis, der A enthielt, ist nicht mehr auf "dem Stapel".

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top