[erledigt]QByteArray richtig kopieren?

Du bist neu in der Welt von C++? Dann schau hier herein!
Antworten
sigvdr
Beiträge: 100
Registriert: 1. Juli 2008 19:29

[erledigt]QByteArray richtig kopieren?

Beitrag von sigvdr » 8. Februar 2014 20:30

Vorgeschichte:
Auf der Suche nach einer Qt Klasse um Metadaten (exif) von Bildern auszulesen bin ich auf QExifImageHeader gestossen. Die stammt aus dem inzw. aufgegebenen Projekt "Qt Extended", einem Versuch von Nokia Qt und Smartphone zusammenzubringen.
In QExifImageHeader habe ich auf diesen Fehler gefunden:

Originalcode: ist Fehlerhaft weil keine Kopie vom QByteArray angelegt wird, somit wird der Inhalt von "value" scheinbar zufällig überschrieben wenn das Original QByteArray ungültig wird.

Code: Alles auswählen

class QExifUndefinedValuePrivate : public QExifValuePrivate
{
public:
    QExifUndefinedValuePrivate( const QByteArray &v )
        : QExifValuePrivate( QExifValue::Undefined, v.size() ), value( v )
    { }
    QByteArray value;
};
für bessere Übersichtlichkeit geändert in: sollte aber exakt dem oberen Code entsprechen.

Code: Alles auswählen

    QExifUndefinedValuePrivate( const QByteArray &v )
        : QExifValuePrivate( QExifValue::Undefined, v.size() )/*, value( v )*/
    { value = v; }
    QByteArray value;
Meine Lösung des Problems

Code: Alles auswählen

    QExifUndefinedValuePrivate( const QByteArray &v )
        : QExifValuePrivate( QExifValue::Undefined, v.size() )/*, value( v )*/
    { value = "";
		value.append(v); 
	 }
    QByteArray value;
Mit value.append(v) erzwinge ich eine Kopie des Dateninhaltes.

Frage:
(C++ Grundlagen) Wie kann eine Kopie einer Klasse "erzwungen" werden, damit mit "value = v" eine Kopie erstellt wird?

Gruß Sig
Zuletzt geändert von sigvdr am 11. Februar 2014 09:04, insgesamt 1-mal geändert.

bmann
Beiträge: 14
Registriert: 22. Juni 2006 22:27
Wohnort: Dortmund

Re: QByteArray richtig kopieren?

Beitrag von bmann » 8. Februar 2014 21:10

sigvdr hat geschrieben:Originalcode: ist Fehlerhaft weil keine Kopie vom QByteArray angelegt wird, somit wird der Inhalt von "value" scheinbar zufällig überschrieben wenn das Original QByteArray ungültig wird.
Ist er nicht. Es wird eine Kopie erstellt.

Qt verwendet hierbei "implicit sharing", siehe http://qt-project.org/doc/qt-5.0/qtcore ... aring.html

Dabei wird die eigentliche Kopie im Speicher erst erstellt, wenn auf eines der Objekte schreibend zugegriffen wird, die "Kopie" ist vorerst nur ein erhöhen des Reference Counters. Funktioniert wunderbar - wenn es bei dir nicht geht, hast du evtl. woanders einen Fehler.
sigvdr hat geschrieben: für bessere Übersichtlichkeit geändert in: sollte aber exakt dem oberen Code entsprechen.

Code: Alles auswählen

    QExifUndefinedValuePrivate( const QByteArray &v )
        : QExifValuePrivate( QExifValue::Undefined, v.size() )/*, value( v )*/
    { value = v; }
    QByteArray value;
Ungefähr, aber nicht exakt.

Im ersten Fall wird beim Erstellen eines QExifUndefinedValuePrivate Objekts der Copy-Konstruktor von QByteArray aufgerufen. Im zweiten Fall der Default Konstruktor, gefolgt von operator=().
Wenn operator=() "normal" implementiert wird (also das tut, was man erwartet) ist das Resultat das Gleiche, aber der erste Fall ist effizienter.
sigvdr hat geschrieben: Meine Lösung des Problems

Code: Alles auswählen

    QExifUndefinedValuePrivate( const QByteArray &v )
        : QExifValuePrivate( QExifValue::Undefined, v.size() )/*, value( v )*/
    { value = "";
		value.append(v); 
	 }
    QByteArray value;
Mit value.append(v) erzwinge ich eine Kopie des Dateninhaltes.
Korrekt, wenn du Glück hast. Wenn QByteArray bemerkt, dass es leer ist (ich weiß grade nicht ob value=""; reicht um das zu verhindern), dann kann es die Kopie aber wieder durch einfaches Erhöhen des Reference Counts ersetzen.
sigvdr hat geschrieben: Frage:
(C++ Grundlagen) Wie kann eine Kopie einer Klasse "erzwungen" werden, damit mit "value = v" eine Kopie erstellt wird?
Gruß Sig
Indem du QByteArray neu schreibst, bzw. selber den Code von QByteArray modifizierst. QByteArray ist so implementiert, dass eine Kopie durch Reference Counting (d.h. implicit sharing) erstellt wird. Wenn du anderes Verhalten benötigst, musst du es selber schreiben, oder "Umwege" nehmen, etwa über deinen QByteArray::append() weg. Eine Alternative wäre es QByteArray::detach() aufzurufen, damit sagst du dem Objekt, dass es sich von dem "shared" Objekt detachen soll, also eindeutig sein soll.

Das ist normalerweise aber nie notwendig, da Qt das selber intern macht, wenn notwendig.

veeman
Beiträge: 267
Registriert: 3. Oktober 2012 01:43
Kontaktdaten:

Re: QByteArray richtig kopieren?

Beitrag von veeman » 8. Februar 2014 21:15

Naja als Fehlerhaft würde ich das jetzt nicht ansehen. Es war wohl so angedacht, dass die Objekte nur gültig sind so lange wie der Buffer constant/gültig bleibt.

Eine Zuweisung ist Implementierungabhängig und erstellt meistens eine Kopie des Objekts.
Im Falle von QByteArray wurde aus Geschwindigkeitsgründen die Zuweisung durch einen impliziert verwalteten Speicherbereich realisiert.

Von daher ist deine Lösung so schon in Ordnung. Ggf. die Memberfunktion clear aufrufen anstatt eine Zuweisung durchzuführen vor dem append.
mfg veeman

mage-dev

Christian81
Beiträge: 7313
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: QByteArray richtig kopieren?

Beitrag von Christian81 » 9. Februar 2014 10:25

Ich sehe in dem geposteten Codestück absolut keinen Fehler. Das Problem muss woanders liegen (z.B. Schreiben in einen const - Buffer mit Hilfe von const_cast<> oder Ähnlichem)
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung

sigvdr
Beiträge: 100
Registriert: 1. Juli 2008 19:29

Re: QByteArray richtig kopieren?

Beitrag von sigvdr » 9. Februar 2014 15:07

So läuft z.B. die Initialisierung von QExifValue(QByteArray());

Code: Alles auswählen

 return QExifValue(QByteArray::fromRawData(header.offsetAscii, header.count));
Was macht "implicit sharing" bei einem QByteArray mit sehr begrenzter Lebensdauer? Das kann doch nur falsch laufen und deshalb ist eine echte Kopie der Daten notwendig.
Eine Alternative wäre es QByteArray::detach() aufzurufen, damit sagst du dem Objekt, dass es sich von dem "shared" Objekt detachen soll, also eindeutig sein soll.
Zu QByteArray::detach() sagt die Qt Doku leider garnichts. Ich gehe mal davon aus dass das so gemeint ist:

Code: Alles auswählen

   QExifUndefinedValuePrivate( const QByteArray &v )
        : QExifValuePrivate( QExifValue::Undefined, v.size() )/*, value( v )*/
    { value = v;
      value.detach();
    }
Ist aber genau das was ich gesucht hatte.

Gruß Sig

Christian81
Beiträge: 7313
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: QByteArray richtig kopieren?

Beitrag von Christian81 » 9. Februar 2014 16:01

Das ist dann einfach eine falsche Benutzung von fromRawData()
Constructs a QByteArray that uses the first size bytes of the data array. The bytes are not copied. The QByteArray will contain the data pointer. The caller guarantees that data will not be deleted or modified as long as this QByteArray and any copies of it exist that have not been modified.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung

RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Re: [erledigt]QByteArray richtig kopieren?

Beitrag von RHBaum » 13. Februar 2014 14:02

Was macht "implicit sharing" bei einem QByteArray mit sehr begrenzter Lebensdauer?
Das selbe wie bei einen QByteArray mit langer Lebensdauer :-) die lebensdauer hat keinen EInfluss drauf :-)

"Normal" macht das impliziet sharing keine Probleme ...

Aber was ist dann "unnormal" ?
In dem Falle:
- DU gibts zeiger auf datenbreiche nach aussen, bzw code macht das impliziet.
- Und an unerwarteter stelle, bzw. an von dir nicht kontrollorbaren Stelle macht irgendwer nen schreibzugriff (auf das orginal oder eine referenz) und vioala, deine Zeiger auf die interne datenbereiche sind nicht mehr das was du erwartest.

Sowas tritt besonders bei verwendung assynchronen funktionen auf die raw zeiger auf datenbreiche haben wollen, und dann mit QBytearrays befuellt werden ... solche funktionen zieht man selber meist durch bibliotheken rein, selber wuerde man immer mit QByteArry direkt arbeiten ^^

Lösungen:
- Kopie auf QByteArray ebene erzwingen (detach, generell zuweissung und CCTor mit QByteArray syntax meiden)
- std::vector oder ähnliches statt QByteArray verwenden ...

In der QT Doku zu Impliziet Sharing steht auch was zu diesen Themen drinne .. wo es probleme machen kann und wie man das eventuell verhindern kann (kopie erzwingen).
Leider ist genau der Teil der doku nicht primär sichtbar, weil man am Anfang soweiso nicht weiß wonach man suchen muss ^^
QExifUndefinedValuePrivate( const QByteArray &v )
: QExifValuePrivate( QExifValue::Undefined, v.size() )/*, value( v )*/
{ value = v;
value.detach();
}

Code: Alles auswählen

QExifUndefinedValuePrivate( const QByteArray &v ):
QExifValuePrivate(QExifValue::Undefined, v.size() ),
value(v.ConstData(),v.size()) // Impliziet sharing aushebeln
{
}
Ciao ...

Antworten