Eigene Stringklasse, char* dynamisch vergrößern
-
Gibt es irgendeinen Grund nicht
std::vectorzu benutzen?private: std::vector<char> str; //... Str::Str(char const* s) : str(s, s + std::strlen(s) + 1) {}Wenn es nicht
vectorsein soll, wäre die nächste Wahlunique_ptr.private: std::unique_ptr<char[]> str; std::size_t size; //... Str::Str(char const* s) : Str(s, s + std::strlen(s)) {} //... Str::Str(char const *begin, char const *end) : size(std::distance(begin, end)) { str.reset(new char[size + 1]); //ein Byte mehr für Nullterminierung std::copy(begin, end, str.get()); str.get()[size] = '\0'; //Nullterminierung für .c_str(), das du wahrscheinlich haben möchtest } Str::~Str() { //nichts, denn das Freigeben macht unique_ptr für dich (wie vector es auch täte) }
-
TyRoXx schrieb:
Gibt es irgendeinen Grund nicht
std::vectorzu benutzen?Ein string kann durchaus recht andere Anforderungen an die Allokationsstrategie haben, als ein std::vector. Darum ist in der Standardbibliothek ein std::string intern auch kein std::vector. Zumindest bei den ueblichen Implementierungen. Ein std::vector wuerde durchaus die Anforderungen an std::string erfuellen, std::string-Implementierungen sind in der Regel trotzdem anders, weil die Anforderungen an std::string (bewusst!) nicht so restriktiv sind wie an std::vector. (Vermutlich hat der TE aber nicht so weit gedacht
)Ob der std::string selber die Aufgabe hat, die Ressourcen zu verwalten oder ob er dies an eine Unterklasse auslagern sollte, darueber laesst sich sicherlich streiten. Der string ist meiner Meinung nach ein Grenzfall zwischen Ressourcenverwalter und einer Klasse, bei der es eher um die sonstige Funktionalitaet geht.
-
SeppJ schrieb:
Ein std::vector wuerde durchaus die Anforderungen an std::string erfuellen, std::string-Implementierungen sind in der Regel trotzdem anders, weil die Anforderungen an std::string (bewusst!) nicht so restriktiv sind wie an std::vector.
Welche Anforderung zum Beispiel?
Umgekehrt erfüllt vector übrigens nicht die Bedingungen für string, weil der Nullterminator fehlt.
-
Ok, swap.
-
enable_if<c++11> schrieb:
Ok, swap.
Genau. Vor C++11 gab's noch mehr Unterschiede, z.B. wann ein Iterator ungueltig werden darf (bei string gab's da mehr Faelle). Diese wurden aber ebenfalls bewusst entfernt, da sie im alten Standard waren, um die Copy-on-Write-Optimierung zu ermoeglichen. Diese Optimierung ist jedoch in Ungnade gefallen, weshalb man den Standard umformuliert hat, um sie nicht mehr zu ermoeglichen.
Der Unterschied beim swap erlaubt es einem std::string beispielsweise, eine short-string-Optimierung einzusetzen, etwas, dass ein std::vector nicht kann.enable_if<c++11> schrieb:
Umgekehrt erfüllt vector übrigens nicht die Bedingungen für string, weil der Nullterminator fehlt.
Das ist keine allgemeine Anforderung an die Eigenschaften eines std::string (der muss nicht nullterminiert sein), sondern an std::string::c_str(), welche ein vector ohnehin nicht hat. Ein String auf Basis von vector muesste diese Methode ohnehin neu implementieren und kann dann ein Nullzeichen anhaengen (oder einfach staendig ein Nullzeichen am Ende halten).
-
SeppJ schrieb:
enable_if<c++11> schrieb:
Umgekehrt erfüllt vector übrigens nicht die Bedingungen für string, weil der Nullterminator fehlt.
Das ist keine allgemeine Anforderung an die Eigenschaften eines std::string (der muss nicht nullterminiert sein), sondern an std::string::c_str(), welche ein vector ohnehin nicht hat. Ein String auf Basis von vector muesste diese Methode ohnehin neu implementieren und kann dann ein Nullzeichen anhaengen (oder einfach staendig ein Nullzeichen am Ende halten).
s.c_str()==s.data() und c_str noexcept und invalidiert keine Iteratoren, daher müssen alle anderen Methoden das Nullzeichen entweder reinschreiben oder Speicher dafür reservieren. Ein String auf Basis von vector müsste daher fast alle Methoden neu implementieren, nicht nur c_str.
-
der muss nicht nullterminiert sein
Doch.
str[str.size()] == 0, und zwar immer. Außerdem zeigtdata()/c_str()direkt auf das interne Array. (Seit C++11, siehe §21.4.7.1 und §21.4.5)sondern an std::string::c_str(), welche ein vector ohnehin nicht hat.
Nein, aber ein vector hat
data(), und das ist dann verschieden.
-
Und was aendert das? Man muss ohnehin neue Methoden schreiben, auch neue Konstruktoren. Die fuegen dann eine Null am Ende ein. Das aendert nichts da dran, dass man eine std::string absolut standardkonform mit einem std::vector als internen Ressourcenhalter implementieren koennte. Dem vector vorzuwerfen, dass er keine automatische Nullterminierung einfuegt ist, wie ihm vorzuwerfen, dass er kein substr oder keinen operator<= anbietet. Das geht voellig am Punkt der Speicherverwaltung vorbei sondern ist Funkionalitaet der Stringklasse und muss von dieser implementiert werden.
Umgekehrt ist es unmoeglich, einen vector intern irgendwie mit einem std::string zu implementieren (ueber irgendwelche wilden reinterpret_casts und placements news), da der string nicht die Anforderungen an vector::swap erfuellen kann.
(Irgendwelche Custom-Allocators lasse ich bei der Diskussion mal aussen vor. Wir reden hier von der normalen Verwendungsweise, bei denen sowohl vector als auch string letztendlich irgendwie ein malloc/new benutzen)
-
SeppJ schrieb:
Umgekehrt ist es unmoeglich, einen vector intern irgendwie mit einem std::string zu implementieren (ueber irgendwelche wilden reinterpret_casts und placements news), da der string nicht die Anforderungen an vector::swap erfuellen kann.
Mit unique_ptr<string> trivial möglich.
-
Man kann sowieso nicht
vectorüberbasic_stringimplementieren, weilbasic_stringnur PODs halten kann (die kein Array sind). Solche Typen sind in Standardklausel 21 als char-like Typen beschrieben.