Kopie bei const std::string(const char*) ?
-
Hallo,
eine ganz kurze Frage:
Erstellt std::string immer eine Kopie des verwendeten const char*,
selbst wenn beide const sind?#include <iostream> #include <string> int main(){ const char* str = "Hallo"; const std::string s(str); const_cast<std::string*>(&s)->operator[](1) = 'e'; std::cout << str << std::endl; std::cout << s << std::endl; }
Hallo
HelloHier hätte ich eigentlich 2mal Hello vermutet oder einen Absturz des Programms.
Merci,
String-Kopierer
-
String-Kopierer schrieb:
Erstellt std::string immer eine Kopie des verwendeten const char*,
Ja.
-
LordJaxom schrieb:
String-Kopierer schrieb:
Erstellt std::string immer eine Kopie des verwendeten const char*,
Ja.
thx
-
Jaaa, Moment. Der Standard schweigt sich darüber aus, wann und unter welchen Umständen std::string wovon Kopien zu machen hat, sondern legt lediglich das Verhalten fest. Es gibt referenzzählende Implementationen von std::string, die erst bei der Veränderung des Strings eine Kopie ziehen.
Mir ist keine Implementation bekannt, die sich im Zusammenhang mit Stringliteralen so verhält, aber plattformabhängig wäre es denkbar, dass eine Implementation bis zum ersten Schreibzugriff einen Zeiger auf das Literal hält; sie müsste es halt nur von anderen Datenbereichen unterscheiden können, die auch als char const * übergeben werden könnten.
-
seldon schrieb:
Mir ist keine Implementation bekannt, die sich im Zusammenhang mit Stringliteralen so verhält, aber plattformabhängig wäre es denkbar, dass eine Implementation bis zum ersten Schreibzugriff einen Zeiger auf das Literal hält;
Das halte ich (auch plattformabhängig) für unmöglich, da der String nicht wissen kann ob der übergebene konstante String überhaupt ein Literal ist. Vgl. folgendes Beispiel:
char ary[] = "0123456789"; char const* cstr = ary; std::string str(cstr); ary[1] = '2';
-
Ja und wenn der Speicherbereich wegfällt? Dann ist der Zeiger ungültig ohne Schreibvorgang:
int main() { std::string* lateString; { const char* hulu = "Guten Morgen allerseits!"; lateString = new std::string(hulu); } std::cout << *lateString << " Und?"; delete lateString; }
Nun ist der Zeiger ungültig, aber wie sollte lateString jemals davon erfahren?
-
Eine platformabhängige Implementierung könnte wissen, in welchem Speicherbereich die statischen Literale liegen, und dann den übergebenen Zeiger prüfen, ob er auf diesen Bereich zeigt. Dann wäre tatsächlich keine Kopie notwendig.
-
Soso.
int main() { std::string* lateString; { char* lalala = new char[10]; strcpy(lalala, "Halli!"); lateString = new std::string(lalala); delete[] lalala; } std::cout << *lateString << " Und jetzt?"; delete lateString; }
-
Der Standard schweigt sich darüber aus, wann und unter welchen Umständen std::string wovon Kopien zu machen hat, sondern legt lediglich das Verhalten fest.
Richtig, allerdings sind die Verhaltens-Anforderungen meist so eng gestrickt, das gewisse Techniken rausfallen.
Vom Interface her, koennte man std::string schon cow implementieren, aber was sagen das zugesicherte Verhalten, wenn man durch nen simples
char & ic = mystring[5];
ne interne Kopie ausloest ?Oder was passiert bei:
std::string s1 = "123456";
std::string s2 = s1;char * mycstring1 = s1.c_str();
s1[5] = '7';
/// wer hat die kopie gemacht ???
char * mycstring2 = s1.c_str();
assert(mycstring1 == mycstring2);Kann der user "erwarten" das wenn er in einem Array eine Zelle Aendert (nichts hinzufügt, nichts löscht) sich der verwendete Speicherbereich aendert ?
Glaub die Dinkumware Implementation zu VS 6.0 Zeiten hat doch sowas gemacht oder ? Da hiess es doch, die STL waer nicht threadsicher ...
Auf alle Fälle muesste das extra Dokumentiert sein. Nen framework, was das verhalten der STL in der Art optimiert, muesste ne eigene Erweiterung zur STL Doku mitbringen !
Ciao ...
-
RHBaum schrieb:
Oder was passiert bei:
std::string s1 = "123456";
std::string s2 = s1;char * mycstring1 = s1.c_str();
s1[5] = '7';
/// wer hat die kopie gemacht ???
char * mycstring2 = s1.c_str();
assert(mycstring1 == mycstring2);Den Teil würdest du gar nicht durch den Compiler bekommen, weil c_str() einen
const char*
zurückliefert. Und ob der Standard eine Lebensdauer für diesen Rückgabewert garantiert, müsste ich auch nachschlagen.
-
Ich zitier dazu mal den Standard:
IOS/IEC 14882:2003 21.3 (5-6) schrieb:
5 References, pointers, and iterators referring to the elements of a basic_string sequence may be invalidated by the following uses of that basic_string object:
— As an argument to non-member functions swap() (21.3.7.8), operator>>() (21.3.7.9), and getline() (21.3.7.9).
— As an argument to basic_string::swap().
— Calling data() and c_str() member functions.
— Calling non-const member functions, except operator[](), at(), begin(), rbegin(), end(), and rend().
— Subsequent to any of the above uses except the forms of insert() and erase() which return iterators, the first call to non-const member functions operator[](), at(), begin(), rbegin(), end(), or rend().
6 [Note: These rules are formulated to allow, but not require, a reference counted implementation. A reference counted implementation must have the same semantics as a non-reference counted implementation.
[Example:string s1("abc"); string::iterator i = s1.begin(); string s2 = s1; *i = 'a'; // Must modify only s1
—end example] —end note]
In RHBaums Beispiel wird die Assertion also fehlschlagen. Natürlich erfordert das bei der Implementation einer referenzzählenden Implementation einiges Fingerspitzengefühl - zum Beispiel komplexere Iteratoren - aber der Standard schließt diese Möglichkeit ausdrücklich nicht aus.
Ansonsten trifft ipsec den Nagel auf den Kopf; es ist in der Tat auf vielen Plattformen möglich, aus der Adresse eines Strings darauf zu schließen, ob es sich um eine Konstante mit statischer Speicherdauer handelt. Mir ist keine Implementation bekannt, die diesen Fall gesondert behandelt, aber völlig unmöglich ist es keinesfalls. Ob in Eisflammes erstem Beispiel der Zeiger ungültig wird, kümmert den String nicht mehr, und das zweite Beispiel nähme in einer solchen hypothetischen Implementation einen anderen Codepfad, weil es sich beim übergebenen Argument nicht um ein Literal handelt.
-
RHBaum schrieb:
Vom Interface her, koennte man std::string schon cow implementieren, aber was sagen das zugesicherte Verhalten, wenn man durch nen simples
char & ic = mystring[5];
ne interne Kopie ausloest ?Der Standard sagt lediglich "konstante Zeit" (23.1.1.12), was auch eine Kopie eines char einschließen kann.