Seite 1 von 1

Temporäre Objekte

Verfasst: 19. März 2009 14:44
von Raien
Hi,

in der Hoffnung ihr könnt mein Kopfzerbrechen beenden schreib ich jetzt einfach mal mein Anliegen hier rein ;)
Also ich kann ja auf einen Button mit

btnTest->setIcon(QIcon("$pfad zum icon"));

ein Icon setzten.
Nur wenn ich jetzt so Nachdenke müsste es doch eig. irgendwann krachen, da mit QIcon("...") ja nur ein Objekt auf dem Stack angelegt wird und nach dem Aufruf von setIcon ja zerstört ist -> beim Anzeigen des Buttons existiert doch das Icon garnicht mehr
Aber irgendwie klappts halt doch genau deswegen frag ich jetzt mal euch =)

Schon mal vielen Dank für die (hoffentlich hilfreichen) Antworten ...

Gruß
Raien

Verfasst: 19. März 2009 14:51
von Christian81
Innerhalb von setIcon() wird das QIcon kopiert, ist also auch später noch vorhanden.

Verfasst: 19. März 2009 16:06
von Raien
Danke!

Dann vll. noch eine kurze Nachfrage :-)
Ist das Kopieren in C++ gängige Praxis bei settern oder nur im Qt-Framework?

Gruß
Raien

Verfasst: 19. März 2009 16:29
von franzf
Es wird nur IN der Methode das Icon einem Member zugewiesen und hierbei kopiert.
Das QIcon wird per Referenz (call by reference vs call by value) übergeben. Falls es bereits existiert, wird nix kopiert.
Und auch bei deinem Beispiel wird ein temporäres Objekt erzeugt und die Adresse an die Funktion übergeben.

Verfasst: 19. März 2009 16:37
von RHBaum
Ist das Kopieren in C++ gängige Praxis
Da streiten sich die Gelehrten ! ^^

Kopien sind am pflegeleichtesten. sie sind multithreading fest und man hat selber die kontrolle ueber die laufzeit. Allerding muss das kopieren ned trivial sein. ab bestimmten groessen oder komplexitaet des zu kopierenden materials kann es zu performance engpaessen kommen.

Dann schwenkt man gern auf referenzen/zeiger um. Das ist aber schwerer zu haendeln und fehleranfeallig.
Um das bissi zu entschaerfen gibt es einige konzepte wo man daten aufn Heap durch objecte aufn Stack verwaltet, und vom user fast behandelt werden koennen wie Stackobjecte.
Dein QIcon iss quasi so nen intelligenter Container.

Technisch ist die Antwort von Christian81 deshalb nur halbrichtig.

Code: Alles auswählen

btnTest->setIcon(QIcon("$pfad zum icon"));

die "kopie" die dabei gemacht werden müsste, kann dabei verzoegert werden (lazy copy, copy on write) so das am anfang erst nur die referenz kopiert wird, also beide QIcon objecte (waehrend dem kopieren) auf die selben Basisdaten zeigen.
Und erst wenn beide Objecte auseinanderlaufen, also eines schreibend auf die daten zugreift, und das andere auf anderer stelle noch drauf verweisst, die eigentliche kopie gemacht wird.
in unserem falle wuerde das temporaere Object sofort nach dem kopieren ungueltig, die daten aber trotzdem im speicher bleiben, da nun das Object dessen setter aufgerufen wurde, wahrscheinlich eine referenz drauf hat noch und damit eigener der daten wuerde ....

In Scriptsprachen verwendet man sowas fast immer ...
In C++ je nach biblio ... wobei die technik in gewissen bereichen zu problemen, bzw expliziete loesungen notwendig macht (multithreading) ...

Ciao ...

Verfasst: 19. März 2009 16:43
von Christian81
Die QIcon-Klasse wird kopiert - d.h. der Copy ctor wird aufgerufen! Dass die Daten der QIcon-Klasse nicht bzw. er später kopiert werden ist dem Nutzer total egal.

Verfasst: 19. März 2009 16:51
von RHBaum
Dass die Daten der QIcon-Klasse nicht bzw. er später kopiert werden ist dem Nutzer total egal.
Vielen Nutzern ja ...

Trotzdem, c++ iss immer noch ne low level Sprache, und im gegensatz zu Java oder Basic laesst es Methoden zu, wo wenn man ned weiss was das macht, gruendlich auf die Schnauze fliegt ^^
Genau so wie manchen Nutzern es nicht egal ist, wann kopiert wird, bei umfangreicheren daten, sondern die es selbst bestimmen muessen.

Wenn die technik so "problemlos" waer, wie Du tust, warum setzen die andere Bibs, wie z.b. die STL oder Boost das ned impliziet ein ?

Ciao ...

Verfasst: 19. März 2009 17:17
von solarix
"implicit sharing" ist problemlos fuer den Benutzer (der Library), nicht fuer den Libraryentwickler selbst.. daher
Wenn die technik so "problemlos" waer, wie Du tust, warum setzen die andere Bibs, wie z.b. die STL oder Boost das ned impliziet ein ?
Weil die Entwickler der anderen Libraries -provokant gesagt- zu faul sind :wink:
Immerhin mussten die Trolls da fuer jede Plattform atomare Referenzzaehler in ASM einbauen, um das ganze performant zu halten... aber wie Christian schon sagte: dem Anwender der Library kann das total egal sein..

Verfasst: 19. März 2009 19:07
von Raien
Hui, danke schonma für die ganzen Antworten =)

Muss jetzt erstma bissi googlen um zu verwerten was da alles kam ;)
Weil irgendwie check ich glaub ich Referenzen noch nicht so ganz...
Eigentlich dachte ich ne Referenz is mehr oder weniger ein Name für eine bestimmte Adresse (im Gegensatz zum Pointer der ja selbst eine hat) aber dann dürfte ich ja einer Referenz rein theopraktisch nix mehr zuweisen dürfen...
Naja habt mir auf jedenfall geholfen

Danke!

Gruß
Raien

Verfasst: 20. März 2009 12:00
von RHBaum
Eigentlich dachte ich ne Referenz is mehr oder weniger ein Name für eine bestimmte Adresse (im Gegensatz zum Pointer der ja selbst eine hat) aber dann dürfte ich ja einer Referenz rein theopraktisch nix mehr zuweisen dürfen...
Stell Dir ne Referenz einfach als fertig dereferenzierter konstanter Zeiger vor ! Nach der initialisierung kannst ihm nix mehr zuweisen.
der Adressoperator bringt natuerlich die einzigste, also die adresse der wirklichen daten zurueck ...
Weil die Entwickler der anderen Libraries -provokant gesagt- zu faul sind
Die Bloedeste Begründung die ich jemals gehoert hab :P Sorry !
(btw, eine bekannte STL Implementierung namens "Dinkumware" haben implizietes cow verwendet und waren deshalb als "eigensinnig und inkompatibel" verschrien ^^ und man ist wieder zurueck.
sogar die libstdc++ (gnu) verwendete dies, tuts aber auch nimmer. Vielleicht hilft das hier:
http://www.open-std.org/jtc1/sc22/wg21/ ... n2647.html )

Code: Alles auswählen

#include <QtCore/QCoreApplication>
#include <QByteArray>
#include <iostream>

int main(int argc, char *argv[])
{
	QByteArray strTest2; 
	const char * pData = NULL;

	{
		QByteArray strTest1 = "Moni";
		strTest2 = strTest1;
		pData = strTest2.constData();
		char * pData = strTest2.data();
	}
        std::cout << strTest2;
	std::cout << pData;	
	int i = 0;
}
was passiert hier wohl ?
Im Standard mit der STL mit std::string ist genau das verhalten genau definiert !
std::cout << strTest2;
std::cout << pData;
muessen beide "Moni" auf die Konsole schreiben !

Ok, Trolltech tut sich da leichter, die sind ned an den standard gebunden.
Schaun wir in die dokumentation:
QByteArray & QByteArray::operator= ( const QByteArray & other )

Assigns other to this byte array and returns a reference to this byte array.
ok, da passiert nichts boeses !
Returns a pointer to the data stored in the byte array. The pointer can be used to access the bytes that compose the array. The data is '\0'-terminated. The pointer remains valid as long as the byte array isn't reallocated or destroyed.

This function is mostly useful to pass a byte array to a function that accepts a const char *.

Note: A QByteArray can store any byte values including '\0's, but most functions that take char * arguments assume that the data ends at the first '\0' they encounter.

See also data() and operator[]().
ok, unser strTest2 sollte nicht zerstoert wurden sein und reallociert haben wir eigentlich auch nix ....

Bleibt die grosse Frage, warum ???
Jeder der die Problematik und die Technik kennt, weiss:
char * pData = strTest2.data();
war der Killer !

dazu in der Doku nur nen lapidarer hinweiss:
For read-only access, constData() is faster because it never causes a deep copy to occur.
Und das ist nur nen recht ueberschaubares Beispiel. In der Praxis iss das alles viel komplexer !
Stell dir mal vor, du musst strings / daten mit Mutexen sichern ... und die dinger arbeiten mit internen cow ohne das was davon weisst?
Ein horrorszenario ! (dies Besipiel ist nicht der Grund, warum die STL Impls z.b. wieder zurueckrudern auf nicht cow, sondern die Begründung, warum der anwender wissen muss, ob cow verwendet wird oder ned.)

Cow ist mit dem Wissen beherschbar und ne gaengige technik, im multithreading einsatz sehr komplex und alles andere als:
dem Anwender der Library kann das total egal sein..
Ciao ....

Verfasst: 20. März 2009 14:17
von solarix
Die Bloedeste Begründung ...
Manchmal fruchten kleine Provokationen in sinnvollen Diskussionen :wink:
Wir muessen hier ja keine Grundsatzdiskussion lostreten.. Weil es in diesem Forum neben dir noch viele andere gibt die Qt in der Praxis (Industrie) einsetzen, trotzdem noch meine Begruendung:

Ich versuche, wo immer moeglich moderne SWE-Prinzipien umzusetzen. Dein Beispiel zeigt zwar moegliche Probleme einer COW-Implementierung, aber gleichzeitig ist es ein super Beispiel, warum man "Information-Hiding" einer Klasse ernst nehmen sollte. Wenn du die Pointer auf die internen Daten nicht speichern wuerdest, wuerde sich das Programm genau so wie erwartet verhalten. In einer reinen Qt-Umgebung funktioniert die Implementierung der Trolls grossartig und auch ueber Threads hinweg. Wenn jeweils komplette Instanzen gespeichert, als Parameter uebergeben oder via Signals versendet werden, gibt es kein "Horrorszenario".

Nun gibts zwei weitere Faelle: der erste ist, wenn die internen Pointer auf die Daten dieser Klassen aus kompatibilitaetsgruenden (z.B. in der Verwendung mit einer anderen Library) benoetigt werden. Genau dafuer bieten die Trolls dieses Interface in QByteArray. Dieser Fall stellt kein Problem dar, solange diese Pointer nur fuer genau diesen Zugriff verwendet und nicht noch irgendwo zwischengespeichert werden.

Der zweite Fall waere eine Library, welche laengerfristig gemeinsame Daten mit der Applikation verwaltet. Beispiel: Ein Bild-Objekt (char*) in einer OpenGL-Library. In diesem Fall trenne ich klar die Qt-Welt von der der verwendeten Library und verwende auch entsprechende Datentypen (in diesem Fall verwalte ich halt den char* selbst). Wieder kein Horrorszenario.

Lange Rede kurzer Sinn: ich bleibe dabei: der Anwender von QByteArray & Co braucht kein Wissen ueber interne Angelegenheiten, ausser er hat einen schlechten Programmierstil (schon wieder eine Provokation :wink: )

Verfasst: 20. März 2009 15:43
von RHBaum
Wir muessen hier ja keine Grundsatzdiskussion lostreten..
Tun wir aber ....
Dein Beispiel zeigt zwar moegliche Probleme einer COW-Implementierung
Richtig, aber ich sag nicht, das man deswegen COW ned nutzen soll. Ich selber brauch es auch recht haeufig, und ich selber hab auch ned bock, das immer selber zu implemntieren.
Warum schreibt dann die Qt nen extra Kapitel in Ihrer Dokumentation, mit ausfuehrlichen Listen welche Klassen das benutzen, und vor allen so wichtige Hinweisse,wie welche Code-Muster im Gegensatz zur STL nicht funktionieren !?
Ich sag nur, das der Nutzer(jeder) schon drueber Bescheid wissen sollte! Wenn er sich nicht fuer sowas interssiert, warum soll er dann c++ schreiben. Da waer er doch bei java / Python oder ähnliches besser aufgehoben !

Ich verweise immer gern auf den Artikel in der C't damals. Genau das entgegengesetzte Beispiel. Da wurde die grundsatzfrage auch gestellt, warum C++/stl und ned Java, oder C# + .net oder Visual Basic. Als Highlight wurde nen grosser Performance Test gemacht ... Und ... C++ war das Schlusslicht (3 mal langsamer als java)!
Warum ? weil sich der Author des Artikels (und damit der (Hobby)Programmierer) keine Gedanken ueber das (Laufzeit)Verhalten gemacht hat. So schoene dinge wie std::map<std::string,std::string> waren da zu sehen.
In dem Falle haette COW der STL performancetechnisch den Arsch gerettet ! Aber das sind ja nur internas die den Programmierer nix angehen. Deshalb ist ja C++ auch um Laengen langsammer als Java !
Und klar, solche Programmierer kommen nie auf den gedanken, c-zeiger zu sichern / cachen, weil man kann doch auch gleich ne kopie machen. Da gibts auch keine Propleme mit den Data() funktionen die zeiger auf internas liefern....
(in diesem Fall verwalte ich halt den char* selbst). Wieder kein Horrorszenario.
Du schreibst dir also nen std::vector<char> selber .... bzw allocierst selber mit new char[], weil Du dich ned fuer intressieren willst, ob das Ding Cow verwendet oder ned ??? Ist das guter Programmierstil ?

Und ich bleib auch dabei, wem Laufzeitverhalten egal ist, der soll Visual basic oder c# oder java verwenden, die machen schon das beste aus dem was einem der Programmierer voersetzt ! :twisted:

Ciao ...

Verfasst: 21. März 2009 20:50
von CLRS530
Da mir die funktionsweise in Qt auch noch ein wenig fremd ist noch eine Frage dazu.
Wenn ich also:

Code: Alles auswählen

QIcon *icon = new QIcon(":/icon/icon.png");
btnTest->setIcon(*icon);
Würde keine weitere Kopie gemacht werden müssen? Ich muss das icon dann aber selbstständig löschen?

Verfasst: 23. März 2009 08:26
von RHBaum
Ja normal ist die "goldene Regel" bei sowas, was du allocierst (new unter c++) musst Du wieder freigeben (delete).

Es gibt natuerlich immer wieder Ausnahmen. Die Qt QObject-Hirarchie ist gleich mal nen grosses Beispiel für !
QIcon gehoert aber nicht dazu (iss nicht von QObject abgeleitet) ...

Die frage ist, ob du ueberhaupt ne dynamische Instanz brauchst ...

Ciao ...