In der modernen C++-Programmierung haben wir die Qual der Wahl, wenn es um die Arbeit mit Textdaten geht. Zwei besonders wichtige Klassen sind std::string
und std::string_view
. Beide dienen dazu, Zeichenketten darzustellen, aber sie tun dies auf grundverschiedene Arten und Weisen. Die richtige Wahl zwischen den beiden kann einen erheblichen Einfluss auf die Performance und Speichernutzung deines Codes haben. Dieser Artikel erklärt dir im Detail, wann du welche Klasse einsetzen solltest, um deinen C++-Code zu optimieren.
Was ist std::string
?
std::string
ist die traditionelle Klasse in C++ zur Verwaltung von Zeichenketten. Sie ist eine eigentümerverwaltete, veränderliche Sequenz von Zeichen. Das bedeutet, dass ein std::string
-Objekt den Speicher für die Zeichenkette selbst besitzt und verantwortlich für die Allokation und Freigabe dieses Speichers ist. Wenn du eine std::string
erstellst, wird Speicher allokiert, um die Zeichenkette aufzunehmen. Wenn du die Zeichenkette änderst (z.B. durch Anhängen, Einfügen oder Löschen von Zeichen), kann der Speicher neu allokiert werden, um die geänderte Zeichenkette aufzunehmen.
Hier sind einige wichtige Eigenschaften von std::string
:
- Eigentümerverwaltet: Die
std::string
besitzt den Speicher, der die Zeichenkette enthält. - Veränderlich: Der Inhalt einer
std::string
kann verändert werden. - Kopierbar: Wenn du eine
std::string
kopierst, wird eine tiefe Kopie erstellt, d.h. es wird neuer Speicher allokiert und der Inhalt der ursprünglichen Zeichenkette in den neuen Speicher kopiert. - Dynamische Speicherallokation: Die Größe des Speichers, der von einer
std::string
verwendet wird, kann sich zur Laufzeit ändern.
Wann solltest du std::string
verwenden?
std::string
ist die richtige Wahl, wenn:
- Du eine eigene Kopie der Zeichenkette benötigst. Wenn du die Zeichenkette bearbeiten musst, ohne die ursprüngliche Zeichenkette zu beeinflussen, ist eine
std::string
erforderlich. - Du die Lebensdauer der Zeichenkette steuern musst. Wenn die Zeichenkette länger als die Lebensdauer der ursprünglichen Quelle (z.B. ein Puffer oder eine Datei) existieren muss, musst du sie in einer
std::string
speichern, um sicherzustellen, dass der Speicher nicht freigegeben wird, bevor du fertig bist. - Du die Zeichenkette ändern musst. Da
std::string
veränderlich ist, ist sie die einzige Wahl, wenn du Zeichen anhängen, einfügen, löschen oder andere Modifikationen vornehmen musst.
Beispiel:
„`cpp
#include
#include
int main() {
std::string message = „Hallo”;
message += „, Welt!”; // Ändern der Zeichenkette
std::cout << message << std::endl; // Ausgabe: Hallo, Welt!
return 0;
}
„`
Was ist std::string_view
?
std::string_view
, eingeführt in C++17, ist eine nicht-eigentümerverwaltete, nicht-veränderliche „Ansicht” (View) auf eine Zeichenkette. Das bedeutet, dass ein std::string_view
-Objekt *keinen* Speicher für die Zeichenkette selbst besitzt. Stattdessen speichert es lediglich einen Zeiger auf den Anfang der Zeichenkette und die Länge der Zeichenkette. Wenn du eine std::string_view
erstellst, wird kein Speicher allokiert (abgesehen vom Speicher für den Zeiger und die Länge). Die std::string_view
„leiht” sich im Wesentlichen den Zugriff auf die Zeichenkette von einer anderen Quelle, z.B. einer std::string
, einem C-String (char*
) oder einem anderen Speicherbereich.
Hier sind einige wichtige Eigenschaften von std::string_view
:
- Nicht-eigentümerverwaltet: Die
std::string_view
besitzt *keinen* Speicher für die Zeichenkette. - Nicht-veränderlich: Der Inhalt einer
std::string_view
kann *nicht* verändert werden. Du kannst zwar die Ansicht verschieben oder verkleinern, aber du kannst die zugrunde liegende Zeichenkette nicht ändern. - Billiges Kopieren: Wenn du eine
std::string_view
kopierst, wird nur der Zeiger und die Länge kopiert. Es wird keine neue Zeichenkette allokiert oder kopiert. - Keine dynamische Speicherallokation: Die
std::string_view
verwendet keine dynamische Speicherallokation.
Wann solltest du std::string_view
verwenden?
std::string_view
ist ideal, wenn:
- Du eine Zeichenkette nur lesen musst. Wenn du die Zeichenkette nicht ändern musst, ist
std::string_view
eine viel effizientere Wahl alsstd::string
. - Du eine kostengünstige Möglichkeit benötigst, auf eine Zeichenkette zuzugreifen. Da
std::string_view
keine eigene Kopie der Zeichenkette erstellt, ist das Erstellen und Kopieren vonstd::string_view
-Objekten sehr schnell. - Du Performance optimieren möchtest. Durch die Vermeidung unnötiger Speicherallokationen und Kopien kann
std::string_view
die Performance deines Codes erheblich verbessern.
Beispiel:
„`cpp
#include
#include
void print_substring(std::string_view str, size_t start, size_t length) {
std::cout << str.substr(start, length) << std::endl;
}
int main() {
std::string message = "Hallo, Welt!";
std::string_view view = message; // Erstellen einer string_view von der string
print_substring(view, 0, 5); // Ausgabe: Hallo
return 0;
}
„`
In diesem Beispiel wird die Funktion print_substring
mit einem std::string_view
als Argument aufgerufen. Die Funktion kann dann die substr
-Methode der std::string_view
verwenden, um einen Teil der Zeichenkette auszugeben, ohne eine neue Zeichenkette zu allozieren. Die substr
-Methode der string_view
gibt *ebenfalls* eine string_view
zurück, nicht eine neue string
.
Performance-Vergleich
Der Hauptvorteil von std::string_view
gegenüber std::string
liegt in der Performance. Die Erstellung und das Kopieren von std::string_view
-Objekten sind viel schneller, da keine Speicherallokationen oder Kopien der Zeichenkette erforderlich sind. Dies kann einen erheblichen Unterschied machen, insbesondere wenn du mit großen Zeichenketten oder in performancekritischen Abschnitten deines Codes arbeitest. Allerdings sollte man bedenken, dass die Lebensdauer der zugrundeliegenden Zeichenkette größer oder gleich der Lebensdauer der string_view
sein muss. Ansonsten zeigt die string_view
auf ungültigen Speicher, was zu undefiniertem Verhalten führt.
Hier ist ein vereinfachtes Beispiel, das den Performance-Unterschied demonstriert:
„`cpp
#include
#include
#include
#include
int main() {
std::string long_string(1000000, ‘a’); // Eine sehr lange Zeichenkette
// Messung der Zeit für die Übergabe von std::string als Wert
auto start_string = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 1000; ++i) {
std::string temp = long_string; // Kopie erstellen
}
auto end_string = std::chrono::high_resolution_clock::now();
auto duration_string = std::chrono::duration_cast(end_string – start_string);
// Messung der Zeit für die Übergabe von std::string_view
auto start_string_view = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 1000; ++i) {
std::string_view temp = long_string; // Keine Kopie!
}
auto end_string_view = std::chrono::high_resolution_clock::now();
auto duration_string_view = std::chrono::duration_cast(end_string_view – start_string_view);
std::cout << "Zeit für std::string (1000 Kopien): " << duration_string.count() << " Mikrosekunden" << std::endl;
std::cout << "Zeit für std::string_view (1000 Ansichten): " << duration_string_view.count() << " Mikrosekunden" << std::endl;
return 0;
}
„`
Dieses Beispiel zeigt deutlich, dass die Übergabe von std::string_view
wesentlich schneller ist als die Übergabe von std::string
(als Wert!), da keine Kopie der Zeichenkette erstellt werden muss.
Zusammenfassung
Die Wahl zwischen std::string
und std::string_view
hängt von deinen spezifischen Anforderungen ab. Hier ist eine kurze Zusammenfassung:
- Verwende
std::string
, wenn du eine eigene Kopie der Zeichenkette benötigst, die Lebensdauer der Zeichenkette steuern musst oder die Zeichenkette ändern musst. - Verwende
std::string_view
, wenn du die Zeichenkette nur lesen musst, eine kostengünstige Möglichkeit benötigst, auf die Zeichenkette zuzugreifen, oder die Performance optimieren möchtest.
Indem du die Unterschiede zwischen diesen beiden Klassen verstehst und die richtige Wahl für deine Situation triffst, kannst du die Performance und Speichernutzung deines C++-Codes erheblich verbessern. Denke daran, die Lebensdauer der zugrundeliegenden Zeichenkette zu beachten, wenn du string_view
verwendest, um undefiniertes Verhalten zu vermeiden.