Matrix mittels Pointer deklarieren
-
hab nochmal ein bisschen im stroustrup gestöbert. der hat mich daran erinnert, dass eine matrix auch als kombination von valarray und slices gebaut werden kann, was mitunter besser ist.
-
camper schrieb:
Werner Salomon schrieb:
Bei näherer Betrachtung ist mir dann auch aufgefallen, dass der Konstruktor nicht exception-sicher ist.
nein? ich denke doch, (sofern der destructor von T nicht wirft, versteht sich).
Hallo Camper,
es hätte gereicht, wenn der (Copy-)Konstruktor von T wirft. Dann wäre das Memory
, m_ptr( new value_type[ nCol * nRow ] )
nicht mehr freigegeben worden, da bei einer Exception im Konstruktor der Destruktor nicht mehr aufgerufen wird.
Gruß
Werner
-
Werner Salomon schrieb:
camper schrieb:
Werner Salomon schrieb:
Bei näherer Betrachtung ist mir dann auch aufgefallen, dass der Konstruktor nicht exception-sicher ist.
nein? ich denke doch, (sofern der destructor von T nicht wirft, versteht sich).
Hallo Camper,
es hätte gereicht, wenn der (Copy-)Konstruktor von T wirft. Dann wäre das Memory
, m_ptr( new value_type[ nCol * nRow ] )
nicht mehr freigegeben worden, da bei einer Exception im Konstruktor der Destruktor nicht mehr aufgerufen wird.
Gruß
Wernernö, new macht das schon richtig. falls in einem new ausdruck eine exception fliegt, werden alle bis dahin bereits konstruierten objekte zerstört und der speicher freigegeben, bevor ein handler in aktion treten kann. andernfalls wäre es prinzipiell nicht möglich, new mit objekten zu benutzen, die werfen können.
-
camper schrieb:
hab nochmal ein bisschen im stroustrup gestöbert. der hat mich daran erinnert, dass eine matrix auch als kombination von valarray und slices gebaut werden kann, was mitunter besser ist.
Stimmt - macht bloß keiner. valarray gehört meiner Meinung nach zu den ungenutzten Featuren des C++-Standards; so wie etwa std::messages.
Wie würdest Du denn eine Matrix mit valarray und slices bauen?
-
camper schrieb:
nö, new macht das schon richtig. falls in einem new ausdruck eine exception fliegt, werden alle bis dahin bereits konstruierten objekte zerstört und der speicher freigegeben, bevor ein handler in aktion treten kann. andernfalls wäre es prinzipiell nicht möglich, new mit objekten zu benutzen, die werfen können.
Stimmt, aber das meine ich nicht. Ich meine vielmehr:
Matrix( int nRow, int nCol ) : m_nCol( nCol ) , m_ptr( new value_type[ nCol * nRow ] ) { // der Speicher ist allokiert // bis hierher sei alles gut gegangen std::fill( m_ptr, m_ptr + nCol * nRow, value_type(0) ); // im fill wird kopiert; evt fliegt hier jetzt eine Exception } // der Konstruktor wird mit mit einer Exception verlassen ..
.. daraufhin werden die Destruktoren der Member aufgerufen. Aber das sind Pointer und int-Typen; da passiert nix. Der Destruktor von Matrix wird nicht aufgerufen und der allokierte Speicher bleibt hängen.
-
Werner Salomon schrieb:
Wie würdest Du denn eine Matrix mit valarray und slices bauen?
keine Lust. Stroustrup macht das in "The C++ Programming Language" schon sehr ausführlich in Abschnitt 22.4 Vektor Arithmetik
Ich bin auch nicht der Ansicht, dass valarray wirklich wenig benutzt wird. es ist nat. auch kein allerwelts-container, sondern mehr für HPC ausgelegt.
-
achso, du meintest deinen konstruktor, nicht meinen vorschlag
nat. könnte und sollte man besser ein boost::scoped_array verwenden. dann hat man dieses problem nicht und auch nicht das eines implizit angelegten copy-ctors.
-
camper schrieb:
achso, du meintest deinen konstruktor
natürlich, zu diesen Zeitpunkt war kein anderer da.
camper schrieb:
nicht meinen vorschlag
welchen Vorschlag meinst Du?
etwa dies:camper schrieb:
zweckmässiger ist ein zusätzlicher parameter und direkte initialisierung (wenn du willst, kannst du auch noch einen haufen template-konstruktoren angeben, die dann alle möglichen parameter aufnehemn, falls T nicht copy-konstruiert werden sollen
Dabei ist mir nicht klar, wie so ein Konstruktor aussehen soll.
Im übrigen hast Du mich jetzt völlig konfus gemacht. Das hier:
Werner Salomon schrieb:
Matrix( size_type nRow, size_type nCol, const value_type& x = value_type() ) : m_size( nRow * nCol ) , m_nCol( nCol ) // Anzahl der Spalten für operator[] merken , m_ptr( new value_type[ m_size ] ) // Memory für Elemente allokieren { try { // die Elemente im Memory anlegen (Konstruktor-Aufruf) std::uninitialized_fill_n( m_ptr, m_size, x );
ist grob falsch; wie Du schon bemerkt hast. Klar bei new value_type[ m_size ] wird der Konstruktor von T aufgerufen .
Mach Du doch mal einen Vorschlag für so einen Konstruktor. 'Keine Lust' gilt jetzt nicht
Gruß
Werner
-
Werner Salomon schrieb:
camper schrieb:
achso, du meintest deinen konstruktor
natürlich, zu diesen Zeitpunkt war kein anderer da.
das 2. codeschnipsel in meinem ersten beitrag.
camper schrieb:
zweckmässiger ist ein zusätzlicher parameter und direkte initialisierung (wenn du willst, kannst du auch noch einen haufen template-konstruktoren angeben, die dann alle möglichen parameter aufnehemn, falls T nicht copy-konstruiert werden sollen
den Normalfall haben wir schon.
eine template-version könnte so aussehen:template<typename I1> Matrix( size_type nRow, size_type nCol, const I1& i1) : m_size( nRow * nCol ) , m_nCol( nCol ) , m_ptr( new value_type[ m_size ]( i1 ) ) { }
das ganze kannst du noch für 2,3,4,5 usw. parameter machen. möglicherweise auch noch noch overloads mit T& - das wird dann allerdings eine ganze menge ;).
-
camper schrieb:
zweckmässiger ist ein zusätzlicher parameter und direkte initialisierung
template<typename I1> Matrix( size_type nRow, size_type nCol, const I1& i1) : m_size( nRow * nCol ) , m_nCol( nCol ) , m_ptr( new value_type[ m_size ]( i1 ) ) { }
Hallo camper,
das übersetzt weder auf MC VC6,7,8 GNU3.2 oder Comeau.
Comeau: error: a new-initializer may not be specified for an array
GNU: error: ISO C++ forbids initialization in array newüberlassen wir doch einfach das Problem mit dem Anlegen und Initialisieren der Elemente der STL. Wir nehmen einfach einen std::vector und setzen voraus, dass hier die Elemente fortlaufend im Speicher liegen. Das steht zwar nicht im Standard, aber wahrscheinlich im nächsten.
#include <vector> template< typename T > class Matrix { public: typedef std::vector< T > container_type; typedef T value_type; typedef typename container_type::size_type size_type; // 'nRow' ist die Anzahl (number) der Zeilen (row); 'nCol' die der Spalten (column) Matrix( size_type nRow, size_type nCol, const value_type& x = value_type() ) : m_nCol( nCol ) // Anzahl der Spalten für operator[] merken , m_coll( nRow * nCol, x ) {} Matrix() : m_nCol( 0 ) , m_coll() {} // der operator[] liefert den Pointer auf das erste Element der Zeile(row) 'iRow' // diese Methode ermöglicht den Zugriff mit // Matrix< Type > mx( nRow, nCol ); // mx[iRow][iCol] = ... ; value_type* operator[]( int iRow ) { return &m_coll[ iRow * m_nCol ]; } const value_type* operator[]( int iRow ) const { return &m_coll[ iRow * m_nCol ]; } private: size_type m_nCol; // Anzahl der Spalten der Matrix container_type m_coll; // Speicher für die Elemente };
Damit wird Matrix jetzt sogar kopierbar und default-constructable. Findet jetzt noch jemand einen Fehler ..
Gruß
Werner
-
ja stimmt, mit arrays kann man keine new-initialisierer liste verwenden.
bleibt noch die variante mit uninitialized fill:boost::scoped_array< char > m_ptr; Matrix( size_type nRow, size_type nCol, const value_type& x = value_type() ) : m_size( nRow * nCol ) , m_nCol( nCol ) // Anzahl der Spalten für operator[] merken , m_ptr( new char[ m_size * sizeof x ] ) // Memory für Elemente allokieren { std::uninitialized_fill_n( reinterpret_cast< value_type* >( m_ptr.get() ), m_size, x ); }
hier muss man wieder selbst für copy-ctor usw. sorgen.
für richtiges alignment sorgt zum glück 5.3.4 Abschnitt 10.
-
noch eine frage dazu...
ist es auch möglich so eine art von matrix, von einer funktion an die nächste zu übergeben, bzw. sie auch als rückgabewert zu setzen?
wenn ja wie?
mfg, TFTS
PS: achso und welcher dieser ganzen codeschnipsel ist jetz die optimale Matrix?
-
TFTomSun schrieb:
noch eine frage dazu...
ist es auch möglich so eine art von matrix, von einer funktion an die nächste zu übergeben, bzw. sie auch als rückgabewert zu setzen?
Definiere Übergeben. Per Referenz oder per Value? Allgemein kann eine beliebige Klasse per Referenz übergeben werden. Die Übergabe per Wert hängt davon ab, wie die entsprechende Klasse ihre Kopier-Semantik handhabt. Die Klasse von Werner kann beides.
wenn ja wie?
Beispiel: Übergabe:
void func(const Matrix<int>& m); // per const-Referenz... void gunc(Matrix<int> m); // per Wert..
Beispiel: Rückgabe:
Matrix<int> func(...) // per Wert... Matrix<int>& func(...) // per Referenz... (wobei du hier keine lokalen Objekte vom Typ Matrix zurückgeben solltest...)
PS: achso und welcher dieser ganzen codeschnipsel ist jetz die optimale Matrix?
Die letzte gepostete Version von Werner ist ganz nett :).
Btw: Schau dir am besten mal das allgemeine Konzept einer Klasse an, insbesondere wovon es abhängt, ob und wie Kopiervorgänge etc. gehandhabt werden.
Gruß Caipi
-
die rückgabe funktioniert leider nicht...
h:\eigene arbeiten\projektarbeit\cprojekte\jpg2bmp\calculate.h(32) : error C2143: syntax error : missing ';' before '<'
an der stelle wo ich die funktion definiere
Matrix<double> BGRToDoubleMatrix(BYTE *pBGRData,int nWidth, int nHeight, int nBitPerPixel, int nElement);
jemand ne idee?
mfg, TFTS
-
ah hat sich erledigt ... ich musste die matrix.h im header meiner calculate klasse includen ... hatte sie vorher nur in der cpp stehen
mfg, TFTS
-
hallo,
hab nochmal eine frage dazu ... ich kann leider nie im debug modus sehen welchen wert gerade meine matrix mx[x][y] hat... kann man das problem irgendwie umgehen?
noch etwas...
wenn ich mir solche matrizen als membervariablen definiere ... kann ich auch später festlegen wie groß die matrizen sein sollen? bzw. kann ich im nachhinein die größe einer matrix ändern?hab noch eine frage ... wie muss ich meine matrix als return value schreiben damit ich sie per const matrix<int>& zürückgeben kann ... ich möchte im prinzip so eine art pointer auf den ausschnitt einer matrix als eine neue matrix zurückgeben ... ohne die werte von der alten matrix in die neue zu kopieren ... geht das?
mfg, TFTS
-
TFTomSun schrieb:
hab nochmal eine frage dazu ... ich kann leider nie im debug modus sehen welchen wert gerade meine matrix mx[x][y] hat... kann man das problem irgendwie umgehen?
Hallo TFTomSun,
Das kommt auf Deinen Debugger an. Bei meinem kann man zum Beispiel folgendes eingeben - angenommen die Matrix-Variable ist 'm' und ich möchte den Inhalt von m[3][7] sehen
m.m_coll._Myfirst + 3 * m.m_nCol + 7
bzw.
*(m.m_coll._Myfirst + 3 * m.m_nCol + 7)
'_Myfirst' ist dabei der Name des Zeigers auf den vector-Anfang in meiner std::vector Pointer-Implementierung. Den musst Du natürlich gegen den Namen in Deiner std::vector-Implementierung austauschen.
Eine komfortablere Möglichkeit wäre eine kleine Hilfsmethode
private: T data( int iRow, int iCol ) { return (*this)[iRow][iCol]; }
muss man noch wissen, dass man diese Methode auch im Code einmal aufrufen muss, also z.B. im Konstruktor von Matrix
template< typename T > class Matrix { public: // usw. Matrix( size_type nRow, size_type nCol, const value_type& x = value_type() ) : m_nCol( nCol ) // Anzahl der Spalten für operator[] merken , m_coll( nRow * nCol, x ) { data( 0, 0 ); // <-- Aufruf, damit der Compiler die Methode auch instanziiert ! }
sonst läßt der Compiler sie evt. weg, weil es sich ja um ein Template handelt und sie nirgendwo benötigt wird.
Und dann im Debugger (bei Watch) einfach eingebenm.data(3,7)
.. vorausgesetzt der kann das !?
TFTomSun schrieb:
noch etwas...
wenn ich mir solche matrizen als membervariablen definiere ... kann ich auch später festlegen wie groß die matrizen sein sollen? bzw. kann ich im nachhinein die größe einer matrix ändern?Member ist kein Problem - Größe ändern geht z.B. durch Überschreiben
Matrix< int > m(3,4); // .. m = Matrix< int >( 12, 100 ); // jetzt ist 'm' größer
natürlich sind dann auch alle alten Werte weg. Wenn Du die behalten willst, solltest Du Dir eine resize-Methode bauen. Das ist aber ein bißchen tricky, da Elemente innerhalb von 'm_coll' hin- und her-kopiert werden müssen, wenn sich die Anzahl der Spalten ändert.
TFTomSun schrieb:
hab noch eine frage ... wie muss ich meine matrix als return value schreiben damit ich sie per const matrix<int>& zürückgeben kann ...
Vorsicht - Du darfst grundsätzlich keine lokalen Variablen per Referenz oder const Referenz zurückgeben. So was wie
[const] Matrix< int >& func1() { Matrix< int > m(..); return m; // <-- GROBER FEHLER - gebe NIE eine lokale Variable per Referenz zurück }
ist immer falsch. Wenn Matrix ein Member einer anderen Klasse bzw. Struct ist, so kannst Du darauf eine const Referenz zurückgeben - so wie gewohnt
class Foo { public: const Matrix< int >& herDamit() const; private: Matrix< int > m_mx; }; const Matrix< int >& Foo::herDamit() const { return m_mx; }
TFTomSun schrieb:
ich möchte im prinzip so eine art pointer auf den ausschnitt einer matrix als eine neue matrix zurückgeben ... ohne die werte von der alten matrix in die neue zu kopieren ... geht das?
Das wird schon schwieriger. Zunächst heißt das ja auch, dass zwei Matrix-Objekte sich einen vector 'm_coll' teilen müßten. Da müßte man noch einiges für tun. Schau Dir vielleicht doch mal die Hilfe zu std::valarray und std::slice usw. an.
Gruß
Werner