Konzept Multiplikationsoperator einer Matrix
-
CStoll schrieb:
Du hast im Kopier-Konstruktor eine lokale Variable "double* data" angelegt, die die Klassen-Variable überdeckt.
PS: Ist es nicht ein wenig sinnlos, die Member transposed und symmetric mit sich selbst du initialisieren?
Super du hast den Fehler gleich gefunden.. und einen weiteren Bug. Danke dir!
-
Ich würde den Destruktor noch auf eine einzige Zeile reduzieren. Denn es ist sehr unwahrscheinlich, dass jemand ihn mehrfach aufruft.
Und der Gleichheitsvergleich mit 0 und der Rückgabe von
newist sinnlos. Denn mit dem letzten Standard sollte ein gescheitertesneweinstd::bad_allocwerfen statt 0 zu sein.
-
EOutOfResources schrieb:
Ich würde den Destruktor noch auf eine einzige Zeile reduzieren. Denn es ist sehr unwahrscheinlich, dass jemand ihn mehrfach aufruft.
Und der Gleichheitsvergleich mit 0 und der Rückgabe von
newist sinnlos. Denn mit dem letzten Standard sollte ein gescheitertesneweinstd::bad_allocwerfen statt 0 zu sein.Sehe ich das richtig dass eine Abfrage auf 0 dann sogar zum falschen Verhalten führen kann?
Also soll ich:
try { new ... } catch(std::bad_alloc &e) { exit(-1); }Könnte man irgendwie diese Matrix Kopiererei (1 Code Zeile 1 bezüglich Zeile 37) irgendwie durch Referenzen unterbinden?
-
neuling21 schrieb:
try { new ... } catch(std::bad_alloc &e) { exit(-1); }Schon besser. Ich würde es in ein Log schreiben oder eine Meldung bringen. Und die abzufangende Exception muss keinen Namen haben.
neuling21 schrieb:
Könnte man irgendwie diese Matrix Kopiererei (1 Code Zeile 1 bezüglich Zeile 37) irgendwie durch Referenzen unterbinden?
Nein, denn es sind lokale Instanzen. In C++0x gibt es Move-Semantics für exakt diesen Fall.
-
Ich glaube ein std::valarray führt bereits genau das aus, was du hier vor hast. (Ich hab mich schon länger nicht mehr mit der Matrizenrechnung befasst, kann sein, dass das völliger Blödsinn ist -> Doku)
Ich glaube ausserdem, dass boost bereits eine fertige Matrizen-Klasse enthält.Ich würde jedenfalls die Größe der Matrix als template-Parameter verwenden, dadurch löst sich das Problem von selbst.
-
EOutOfResources schrieb:
neuling21 schrieb:
try { new ... } catch(std::bad_alloc &e) { exit(-1); }Schon besser. Ich würde es in ein Log schreiben oder eine Meldung bringen. Und die abzufangende Exception muss keinen Namen haben.
neuling21 schrieb:
Könnte man irgendwie diese Matrix Kopiererei (1 Code Zeile 1 bezüglich Zeile 37) irgendwie durch Referenzen unterbinden?
Nein, denn es sind lokale Instanzen. In C++0x gibt es Move-Semantics für exakt diesen Fall.
Danke dir schonmal für deine Hilfe.
Meinst du mit Move Sematics so etwas?
constexpr lapack_matrix lapack_matrix::operator*(lapack_matrix &rhs) constDieses Rumkopieren ist bisher der einzige Wehmutstropfen. Man spürt eine deutliche Verzögerung bei größeren Matrizen gegenüber nativem LAPACK
-
Da wäre es eventuell eine Idee, deine Matrix-Klasse mit Referenzzhälung und copy-on-write Funktionalität auszustatten. (d.h. der Speicher wird erst wirklich kopiert, wenn einer der Verwender etwas daran ändern will)
-
neuling21 schrieb:
constexpr lapack_matrix lapack_matrix::operator*(lapack_matrix &rhs) constNein:
lapack_matrix&& lapack_matrix::operator*(lapack_matrix &rhs) constUnd wie ich bereits gesagt habe (lesen?) sollst du den Parameter und den Rückgabewert
constmachen!
-
CStoll schrieb:
Da wäre es eventuell eine Idee, deine Matrix-Klasse mit Referenzzhälung und copy-on-write Funktionalität auszustatten. (d.h. der Speicher wird erst wirklich kopiert, wenn einer der Verwender etwas daran ändern will)
Ist es so etwas wie ein proxy pattern? Im worst case hätte ich aber immer noch dieses herumkopiere..
-
EOutOfResources schrieb:
neuling21 schrieb:
constexpr lapack_matrix lapack_matrix::operator*(lapack_matrix &rhs) constNein:
lapack_matrix&& lapack_matrix::operator*(lapack_matrix &rhs) constUnd wie ich bereits gesagt habe (lesen?) sollst du den Parameter und den Rückgabewert
constmachen!Wurde sofort gemacht
Nur habe ich es hier im Forum aus dem oberen Beitrag kopiert. Danke
-
neuling21 schrieb:
Ist es so etwas wie ein proxy pattern? Im worst case hätte ich aber immer noch dieses herumkopiere..
Bei den Pattern bin ich nicht 100% sattelfest, aber in dieser Anwendung ersparst du dir das Kopieren auf jeden Fall (das Original, von dem kopiert wird, verschwindet sofort im Nirvana - und am Ende ist C der einzige, der die Daten verwendet).
In den Worst-Case-Fällen, wo du um die tatsächliche Kopie nicht herumkommst, hättest du vermutlich auch mit dem C-Code, den du hier versteckst, deinen Spiecherbereich kopieren müssen, also dürfte das kein so großes Proble sein:
lapack_matrix M1 = ...; lapack_matrix M2 = M1; M2(0,0) = -1;//hier wird tatsächlich eine Kopie notwendig
-
EOutOfResources schrieb:
lapack_matrix&& lapack_matrix::operator*(lapack_matrix &rhs) constUnd wie ich bereits gesagt habe (lesen?) sollst du den Parameter und den Rückgabewert
constmachen!Wurde sofort gemacht
Nur habe ich es hier im Forum aus dem oberen Beitrag kopiert. Danke[/quote]Entschuldige dass ich nochmal hinterfragen muss aber ich bekomme im oberen Code diesen fehler. Selbst wenn ich const lapack_matrix tmp(...) mache.
error C2440: 'return' : cannot convert from 'lapack_matrix' to 'const lapack_matrix &&' You cannot bind an lvalue to an rvalue referenceGibt es schon ein Buch dass in den neuen Standard reinschnüffelt solche tips gut beschreibt und zudem möglichst deutsch (einfaches englisch) ist?
-
Der Standard ist noch nicht verabschiedet. Welchen Compiler hast du?
-
CStoll schrieb:
Bei den Pattern bin ich nicht 100% sattelfest, aber in dieser Anwendung ersparst du dir das Kopieren auf jeden Fall (das Original, von dem kopiert wird, verschwindet sofort im Nirvana - und am Ende ist C der einzige, der die Daten verwendet).
In den Worst-Case-Fällen, wo du um die tatsächliche Kopie nicht herumkommst, hättest du vermutlich auch mit dem C-Code, den du hier versteckst, deinen Spiecherbereich kopieren müssen, also dürfte das kein so großes Proble sein:
lapack_matrix M1 = ...; lapack_matrix M2 = M1; M2(0,0) = -1;//hier wird tatsächlich eine Kopie notwendigDas hört sich gut an. Auch die Klammer als Elementzugriffsoperator zu gebrauchen... hab gar nicht daran gedacht.
Ich führe also in jeder Klasse einen Zeiger auf lapack_data der ersten Matrix:
class lapack_data { public: double *data; unsigned int ref; }und im Destruktor
lapack_matrix::~lapack_matrix { if( ((*lapack_data)--) == 0 ) { delete lapack_data; } }Bei lesenden Funktionen greife ich ganz normal auf double * zu. Sobald ich einmal etwas ändere Rufe ich eine Funktion auf die:
-mir den double Zeiger kopiert.
-Referenz herunterzählt.
-Wenn Referenz == 0 dann lapack_data löscht
Habe ich dich richtig verstanden?
-
EOutOfResources schrieb:
Der Standard ist noch nicht verabschiedet. Welchen Compiler hast du?
MS Visual Studio 2010 SP1
-
neuling21 schrieb:
und im Destruktor
lapack_matrix::~lapack_matrix { if( ((*lapack_data)--) == 0 ) { delete lapack_data; } }eher
(--lapack_data->ref)==0, aber ansonsten sieht es so richtig aus.
Du solltest nur daran denken, daß Kopier-Konstruktor und operator= das Spiel auch mitmachen.Bei lesenden Funktionen greife ich ganz normal auf double * zu. Sobald ich einmal etwas ändere Rufe ich eine Funktion auf die:
-mir den double Zeiger kopiert.
-Referenz herunterzählt.
-Wenn Referenz == 0 dann lapack_data löscht
Habe ich dich richtig verstanden?Fast - wenn ref==1 ist, brauchst du nicht zu kopieren, sondern kannst normal mit den vorhandenen Daten arbeiten.
-
EOutOfResources schrieb:
Nein:
lapack_matrix&& lapack_matrix::operator*(lapack_matrix &rhs) constWie kommst du nur auf die Idee, eine RValue Referenz auf eine lokale Variable zurückzugeben. RValue Referenzen _sind_ auch nur Referenzen! Eine ganz normale Wert-rückgabe, allerdings mit move, gehört hier her.
Move-Semantiks bedeutet, dass es einen Move Ctor gibt, der Move op = kann über den Move Ctor sehr einfach implementiert werden, indem man das Objekt per Value nimmt.
-
314159265358979 schrieb:
EOutOfResources schrieb:
Nein:
lapack_matrix&& lapack_matrix::operator*(lapack_matrix &rhs) constWie kommst du nur auf die Idee, eine RValue Referenz auf eine lokale Variable zurückzugeben. RValue Referenzen _sind_ auch nur Referenzen! Eine ganz normale Wert-rückgabe, allerdings mit move, gehört hier her.
Move-Semantiks bedeutet, dass es einen Move Ctor gibt, der Move op = kann über den Move Ctor sehr einfach implementiert werden, indem man das Objekt per Value nimmt.
Wäre dir für ein Beispiel sehr dankbar.
-
Move-Konstruktor:
Object(Object&& other) : some_variables(other.some_variables) { other.some_variables = some_default_value; }Hier werden die Ressourcen von other "geklaut". Wichtig ist dabei, dass other weiterhin ein gültiges Objekt ist, mit dem gearbeitet werden kann.
op =:
Object& operator = (Object rhs) // ganz normale Werte-übergabe { rhs.swap(*this); return *this; }Diese operator = kann sowohl kopieren, als auch moven. Er wird deswegen auch gerne als "unified assignment-operator" bezeichnet. Wenn ein R-Value übergeben wird, wird das rhs-Objekt move-constructed, sprich, der Move-Konstruktor wird für rhs aufgerufen. Wird ein L-Value übergeben, dann wird der normale Copy-Ctor aufgerufen.
Mit diesem assignment operator entfällt auch der nervige Check, ob other == *this, außerdem ist er Excepion-Save, ein swap() sollte niemals etwas werfen.
-
CStoll schrieb:
Da wäre es eventuell eine Idee, deine Matrix-Klasse mit Referenzzhälung und copy-on-write Funktionalität auszustatten. (d.h. der Speicher wird erst wirklich kopiert, wenn einer der Verwender etwas daran ändern will)
Oder nur C++98-Move-Semantik à la
auto_ptr, sofern man keine Wertsemantik benötigt. Ist noch etwas günstiger alsintrusive_ptr, was hier ja im Prinzip nachgebaut wird.314159265358979 schrieb:
Eine ganz normale Wert-rückgabe, allerdings mit move, gehört hier her.
Ohne
std::move(), eine funktionslokale Variable existiert nicht länger. Move-Konstruktoren können implizit generiert und aufgerufen werden.Aber heutige Compiler können schon recht gut RVO. Allerdings muss man ein paar Dinge beachten, um die Optimierung nicht zu verhindern.