Kopieren bei der Rückgabe verhindern



  • Halli!

    Ich bin gerade dabei, eine kleine String-Klasse (CString) zu schreiben. Dabei bin ich auf ein Problem gestoßen. Ich hätte gerne eine Member-Funktion "SubString(unsigned int index, unsigned int count)", welche einen CString zurückgibt. Bisher habe ich es so gemacht, dass ich im Funktionsrumpf einen CString erstellt, darein den gewünschten Substring geschrieben und diesen dann zurückgegeben habe. Dieser wird bei der Übergabe ja aber nochmal in voller Länge kopiert, oder? Diesen Aufwand möchte ich gerne vermeiden.

    CString cs("Hallo da");
    CString cs2;
    cs2 = cs.SubString(6,2);  // An dieser Stelle wird ein CString mit dem
                              // Inhalt "da" innerhalb der Klasse erstellt
                              // und zurückgegeben
    

    Wie kann ich das kopieren vermeiden?



  • Gar nicht. Wenn der Benutzer nur einen Teilbereich innerhalb des Strings will hat er den ja eh schon durch die Angabe des Bereichs (des Substrings).



  • das wird automatisch vom compiler wegoptimiert.



  • @dragon: ?? Verstehe ich nicht. Sorry.

    @.....: Und was habe ich davon? Ich würde das gerne selber wegoptimieren.





  • Und was habe ich davon? Ich würde das gerne selber wegoptimieren.

    dann sieht dein quellcode aber nicht mehr so schön aus



  • Hey, OK. Dann muss ich mich wohl wirklich auf den Compiler verlassen. Vielen Dank, ihr drei.



  • achso dachte du redest von der Kopie beim Erzeugen des Substrings nicht von der doppelten Kopie durch den Rückgabewert. Die übernimmt wie bereits gesagt dein Compiler und die kann auch nur der übernehmen, du hast da keine Möglichkeit das selbst zu machen.


  • Mod

    CString cs("Hallo da");
    CString cs2;
    cs2 = cs.SubString(6,2);
    

    hier wird nicht kopiert (im sinne von copy-konstruktion), sondern zugewiesen. diese zuweisung kann und darf der compielr nicht wegoptimieren. etwas anderes wäre

    CString cs2( cs.SubString(6,2) );
    

    direkte initialisierung oder

    CString cs2 = cs.SubString(6,2);
    

    copy-initialisierung, was hier keinen unterschied macht. im falle einer initialisierung kann und darf der compiler ein eventuell entstehendes temporäres objekt, das nur dazu benutzt wird, ein anderes objekt zu initialisieren, eliminieren. und C++ bevorzugen wir sowieso grundsätzlich initialisierung gegenüber zuwesiung (und irgendwann wird das auch der letzte begreifen). nun können wir nicht in jedem falle auf initialisierung zurückgreifen, manchmal müssen wir zuweisen. in diesem falle ist die eliminierung (auch mit unterstützung des programmieres) innerhalb der gegenwärtigen sprache nicht immer möglich. was wir nicht können, ist die entstehung eines temporären objektes zu verhindern. was möglicherweise elemniert werden kann, sind kostspielige deep-copy operationen. da das temporäre objekt nach der zuweisung sowieso nur noch zerstört wird, bietet es sich an, dem objekt die resourcen quasi zu stehlen, ganz so wie es auto_ptr tut. eine in der anwendung invasive - dafür simple - mehode bestünde in der benutzung einer swap-memberfunktion (so exiustent).

    cs.SubString(6,2).swap( c2 );
    

    oder eine explizite move-funktion

    cs.SubString(6,2).move_to( c2 );
    

    nicht besonders schön und expressiv - aber zumindest einfach zu implementieren. ein lösung, die die syntax der anwendung nicht verändert, ist mit den gegenwärtigen sprachmitteln nur eingeschränkt möglich:
    da wäre einmal Alexandrescu's mojo-framework
    nachteile:
    - das interface der klassen, die es benutzen muss verändert werden
    - teilweise benutzung von konversionspfaden mit zwei nutzerdefinierten konvertierungen (was der standard verbietet), das ist die krankheit, unter der auch auto_ptr leidet

    zum anderen:
    Clarification of Initialization of Class Objects by rvaluesdas auf einer bestimmten interpretation des gegenwärtigen standards basiert.
    vorteil: relativ leicht zu implementieren
    nachteil: wird nur von wenigen compilern unterstützt (g++: ja, msvc: nein)

    allgemein lösbar wird das probvlem wohl erst mit dem nächsten standard nach der einführung von rvalue-referenzen sein.



  • Momentan kann WebFritzi sich zumindest mit einer nested Proxy Klasse behelfen.

    class CString
    {
    private:
    	class SubStringProxy
    	{
    	private:
    		const CString* instance_;
    		size_t offset_;
    		size_t length_;
    	public:
    		SubStringProxy(const CString* instance, size_t offset, size_t length)
    			: instance_(instance)
    			, offset_(offset)
    			, length_(length)
    		{
    		}
    	};
    public:
    	SubStringProxy SubString(size_t offset, size_t length) const
    	{
    		return SubStringProxy(this, offset, length);
    	}
    	CString& operator =(const SubStringProxy& rhs)
    	{
    		// hier wird kopiert
    		return *this;
    	}
    };
    

    Ist zwar auch noch nicht optimal, so lässt sich aber zumindest der aufwändige temporäre String vermeiden.


Log in to reply