002
22.10.2003, 16:19 Uhr
virtual
Sexiest Bit alive (Operator)
|
const kann ganz erhebliche Performance unterschiede nach sich ziehen. Mal ein kleiner Exkurs:
Jeder Container bringt jeweils zwei Versionen der Methoden begin() und end() mit: einmal eine, die einen const_iterator zurückliefert, mal eine, die einen normalen iterator zurückliefert. Ich habe mal eine kleines Klasse geschrieben, die ausgibt, welcher der beiden Versionen jeweils aufgerufen wird:
C++: |
/* mystr.h */ class mystr { std::string str; public: std::string::const_iterator begin() const { std::cout<<"const begin()"<<std::endl; return str.begin(); } std::string::iterator begin() { std::cout<<"non-const begin()"<<std::endl; return str.begin(); } std::string::const_iterator end() const { std::cout<<"const end()"<<std::endl; return str.end(); } std::string::iterator end() { std::cout<<"non-const end()"<<std::endl; return str.end(); }
mystr(const char*s) :str(s) { } };
|
Die spannende Frage ist nun, wann welche der beiden Versionen nun aufgerufen wird. Die antwort ist einfach: wird ein Parameter mt const übergeben, wird die const variante genommen, ansonsten die non-const:
C++: |
#include <iostream> #include <string> #include <algorithm> #include "mystr.h"
void f(const mystr& str) { std::copy(str.begin(), str.end(), std::ostream_iterator<char>(std::cout)); }
int main() { mystr str = "Hallo, Welt\n"; f(str); }
|
Hier wird ein const parameter erwartet und die Ausgabe ist:
Code: |
const end() const begin() Hallo, Welt
|
Lasse ich das const beim f weg, also:
dann ist die Ausgabe
Code: |
non-const end() non-const begin() Hallo, Welt
|
Es werden also unterschiedliche Methoden aufgerufen. Und das hat krasse Auswirkungen: Viele STL Implementationen arbeiten nach dem COW (=Copy On Write) Prinzip. Dh wenn man zB eine Stringvariable nimmt und kopiert:
C++: |
std::string str1 = "Hallo"; std::string str2 = str1;
|
Teilen sich str1 und str2 solange den Speicher für "Hallo", bis einer der beiden Strings verändert wird. Erst dann wird - beim ersten Schreibzugriff - eine Koipie für beispielsweise str2 angelegt, in der die durch den Schreibzugriff erforderliche Änderung gemacht wird. (Der Grund ist, daß man sehtr häufig Objekte einfach nur kopiert ohne sie zu verändern. In diesen Fällen spart das COW jede Menge Speicher und Rechenzeit).
Ein const_iterator ist ein Iterator, der niemals Schreibzugriffe erlaubt, der nicht const iterator also schon. In der Regel werden also Container mit COW Algorithmus, fordert man von ihnen einen non-const iterator an, sicherstellen, daß sie eine eigene Kopie des Containes haben. Würde also das main von oben so aussehen:
C++: |
int main() { mystr str2 = "Hallo, Welt\n"; mystr str = str2; f(str); }
|
und die std::string Implemetierung COW verwenden, wäre die nocht const variante deutlich langsameer.
Aber natürlich hat auch geissbock recht: const erhöht die Lesbarkeit! -- Gruß, virtual Quote of the Month Ich eß' nur was ein Gesicht hat (Creme 21) |