Werte aufnehmen, als vector speichern und in Klasse verpacken
-
ich möchte Messwerte von einem Gerät aufnehmen und anschließend speichern. Da es mehrere Messwerte für eine Messreihe sind, sollen diese in einem Vektor abgespeichert werden. Diese sollen wiederum in einer Klasse zusammengefasst werden. Da noch andere Messwerte aufgenommen werden sollen und auch die Messtechnik mit eingestellt werden soll läuft alles über Threads. Abschlißend sollen die Werte noch gespeichert werden. Wie würdet Ihr da vorgehen?
Da ich ein Programm in diesem Umfang noch nicht geschrieben habe, ist mir das richtige Vorgehen noch nicht bekannt.
Ich will das ersteinmal als Beispielprojekt machen. Meine Messwerte sollen MesswertA und MesswertB heissen.
Ich würde zuerst eine Klasse ClassMesswerte schreiben:class ClassMesswerte { private: double MesswertA; double MesswertB; public: };
wie mach ich das jetzt mit den vektoren?
wie man sie definiert und damit umgeht weiss ich, nur wie verpack ich diese in die Klasse?vector<double>v_MesswertA; vector<double>v_MesswertB;
erzeug ich die Vektoren in der Klasse, in dem Thread oder in Unit1?
-
Hallo
Du kannst in einen vector jeden kopierbaren Datentyp packen, auch eigene Klassem. Auch ein Kopierkonstruktur ist gut. Also sollte das bei dir so aussehen :
vector<ClassMesswerte> v_Messwerte;
Ich empfehle dir dringend, für die Klasse auch noch einen ordentlichen Konstruktor zu schreiben, die die beiden double-Werte initialisiert.
class ClassMesswerte { public : ClassMesswerte(double A = 0.0d, double B = 0.0d) : MesswertA(A), MesswertB(B) { } ClassMesswerte(const ClassMesswerte& a) : MesswertA(a.MesswertA), MesswertB(a.MesswertB) { } ... };
bis bald
akari
-
ist es empfehlenswert dies alles in die Threadklasse mitreinzuschreiben? dort gibt es ja auch schon einen Konstruktor:
class Messwerte : public TThread { private: protected: void __fastcall Execute(); public: __fastcall Messwerte(bool CreateSuspended); };
-
Hallo
Jede Klasse hat ihren eigenen Konstruktor (selbst wenn du im Quellcode keinen hinschreibst). Ob dein Thread bereits einen Konstruktor hat oder nicht ist für deine Messwert-Klasse irrelevant.
Es ist gut den Thread von den reinen Daten im Speicher zu trennen. Der Thread muß dann die Messwert-Klasse kennen, aber die Messwert-Klasse nicht den Thread.Und verwende dann auch aussagekräftigere Namen, alles Messwert zu nennen hilft nicht weiter.
bis bald
akari
-
ich kenn diese Art von Initialisierung gar nicht. Ich initialisiere so:
class ClassMesswerte { private: double MesswertA; double MesswertB; public: ClassMesswerte(double MesswertA = 0.0, double MesswertB = 0.0) { this->MesswertA = MesswertA; this->MesswertB = MesswertB; }
-
das mit den Namen ist gar nicht so einfach
-
Hallo
rudpower schrieb:
ich kenn diese Art von Initialisierung gar nicht. Ich initialisiere so: ...
Nennt sich Initialisierungsliste. Ist deiner Variante vorzuziehen.
bis bald
akari
-
der erste Konstruktor ist jetzt klar, dort bekommen die beiden Variablen einfach Anfangswerte zugewiesen.
Doch der zweite. Hier wird gleich ein objekt erzeugt? und wie ich sehe eine Referenz erstellt.
-
Hallo
Das ist der Kopierkonstruktor. Dieser wird eingesetzt wenn du schreibst :
Messwert A(1,2); // Normaler Konstruktor für A Messwert B = A; // Kopierkonstruktor für B : kopiert die Werte aus A in B
bis bald
akari
-
gibts nicht eine einfachere Möglichkeit? Ich sehe keinen Vorteil, im Gegenteil, so wird das Programm doch noch komplexer, vor allem wenn noch mehr Messwerte dazukommen. Ich weiss auch nicht was jetzt im Header steht. In der cpp steht bei mir jetzt:
Messwerte(double T_, double PH_) : temperatur(T_), pHWert(PH_) { } Messwerte(const Messwerte &a) : //Kopierkonstruktor temperatur(a.temperatur), pHWert(a.pHWert) { }
Wie geh ich am besten vor?
-
Wieso wird dadurch das Programm komplexer? Die Anzahl der Zuweisungen ändert sich nicht, die Initialisierungsliste wird unbedingt benötigt wenn Basismember oder konstanten Member initialisiert werden müssen. Und den Kopierkonstruktor benötigst du nun mal bei der Copy-Semantik der Standardcontainer.
Vllt wäre auch ein weiterführendes C++-Buch sinnvoll für dich.
-
witte schrieb:
Und den Kopierkonstruktor benötigst du nun mal bei der Copy-Semantik der Standardcontainer.
Jein. Normalerweise erzeugt der Compiler den Kopierkonstruktor und den Zuweisungsoperator automatisch, dabei werden alle Member bitweise kopiert. Borland/Codegear brät da wieder eine Extrawurst, die bei der Verwendung des __property Schlüsselworts für den Zugriff auf Member eine explizite Implementation beider Methoden verlangen.
Back to topic:
Die Komplexität eines Kopierkonstruktors im Vergleich zu Threadsynchronisation (die du ebenfalls benötigst) ist quasi null. Da kommt mit Sicherheit noch einiges auf dich zu, das alles andere als trivial ist.
-
hab mir bereits mehrere Bücher besorgt, darunter u.a. das Standardwerk "C++ mit dem Borland C++ Builder" von Richard Kaiser wo ich 70 Euros hingelegt hab. Leider wird das irgendwann sehr tiefgreifend und unverständlich.
-
die Frage ist wie ich das auf 2 Dateien aufteile, also Header und cpp?
-
Naja, das sollte doch hinzukriegen sein
Woran hakt´s denn konkret?
-
bei mir haperts schon beim Kopierkonstruktor, dessen Verwendung ich nicht verstehe. Ich möchte ja eigentlich nur, dass meine Messwerte in einer Klasse zusammengefasst werden. Wie man eine normale Klasse schreibt, den Konstruktor verwendet versteh ich.
-
Das mit dem Kopierkonstruktor ist eigentlich ganz einfach. Du brauchst einen Kopierkonstruktor nur dann, wenn einer von zwei Fällen auftritt:
- Deine Klasse enthält member, die nicht bitweise kopiert werden dürfen, sondern spezielle Massnahmen beim Kopieren erfordern.
Beispiel (realitätsfern):
class MyClass { char* Name_; public: MyClass( char* Name ) { // Puffer erzeugen und Namen kopieren Name_ = new char[strlen( Name +1 )]; strcpy( Name_, Name ); } ~MyClass() { delete[] Name_; } }; int main() { MyClass obj1; MyClass obj2 = obj1; }
Ohne speziellen Zuweisungsoperator/Kopierkonstruktor passiert hier Folgendes: Bei der Erzeugung von obj2 wird der Kopierkonstruktor benutzt, da er nicht explizit implementiert ist erzeugt der Compiler ihn automatisch. Der automatisch erzeugte Kopierkonstruktor kopiert alle member bitweise, was zur Folge hat, dass der Pointer Name_ aus obj1 und Name_ aus obj2 auf den gleichen Speicherbereich zeigen. Beim Verlassen der main() Funktion wird obj2 zerstört, was ein delete[] auf den Pointer Name_ zur Folge hat und den Speicher wieder freigibt. Dann wird obj1 zerstört, was ebenfalls ein delete[] auf Name_ bedeutet. In diesem Fall wird der gleiche Speicherbereich zwei Mal freigegeben (weil obj1.Name_ und obj2.Name_ auf die gleiche Adresse zeigen), was Undefinierte Verhalten nach sich zieht. Um das zu verhindern muss der Kopierkonstruktor und der Zuweisungsoperator diesen Sonderfall verhindern, was dann so aussähe:
class MyClass { // Code von oben steht hier MyClass( const MyClass& op ) { // tiefe Kopie erzeugen und nicht nur Pointer übernehmen Name_ = new char[strlen( op.Name_ ) +1]; strcpy( Name_, op.Name_ ); } MyClass& operator=( const MyClass& op ) { // Selbstzuweisung verhindern if( this != &op ) { // Kopiervorgang exception-safe machen durch Verwendung // einer temp. Variablen char tmp = new char[strlen( op.Name_ ) +1]; strcpy( tmp, op.Name_ ); delete[] Name_; Name_ = tmp; } return *this; } };
Als Faustregel gilt: Wenn man einen speziellen Destruktor, Kopierkonstruktor oder Zuweisungsoperator braucht, dann braucht man alle drei.
- Deine Klasse enthält Attribute, die mit dem Schlüsselwort __property zugänglich gemacht werden. Aus irgendwelchen Gründen verweigert der Compiler dann die automatische Erzeugung des Kopierkonstruktors und des Zuweisungsoperators (die er sonst automatisch erzeugen würde). Ist eine reine Fleissarbeit, bei der man lediglich alle member kopieren muss. Vielleicht hat audacia ein Ahnung, warum der KK und ZO nicht automatisch erzeugt werden kann.
class MyClass { double ValueA_; double ValueB_; public: MyClass() : ValueA_( 0.0 ), ValueB_( 0.0 ) { } MyClass( const MyClass& op ) : ValueA_( op.ValueA_ ), ValueB_( op.ValueB_ ) { } MyClass& operator=( const MyClass& op ) { ValueA_ = op.ValueA_; ValueB_ = op.ValueB_; return *this; } // Grund für den ganzen Humbug: __property double ValueA = { read=ValueA_, write=ValueA_ }; __property double ValueA = { read=ValueB_, write=ValueB_ }; };
Zu deiner Frage:
Pack die Deklaration der Messwert Klasse in eine Datei (z.B. Messwert.h) und die Implementation in die entsprechende .cpp Datei (Messwert.cpp). Überall, wo du nun die Klasse Messwert benutzen willst fügst du die include Anweisung für Messwert.h ein.
-
verstanden hab ich das noch nicht.
warum müssen die objekte (das wort member steht wohl für Objekte?) kopiert werden? Ich will doch nur meine Messdaten in einer Klasse haben. Warum reicht es da nicht einfach alle Messdaten einfach nur in die Klasse aufzunehmen?
-
rudpower schrieb:
verstanden hab ich das noch nicht.
warum müssen die objekte (das wort member steht wohl für Objekte?) kopiert werden? Ich will doch nur meine Messdaten in einer Klasse haben. Warum reicht es da nicht einfach alle Messdaten einfach nur in die Klasse aufzunehmen?Member sind die Elemente einer Klasse, in meinem Beispiel oben hat die Klasse MyClass genau zwei member: ValueA_ und ValueB_.
Sie müssen kopiert werden, weil std::vector (und eigentlich alle STL Container) intern mit Kopien arbeiten. Du fügst mit push_back() nicht das übergebene Objekt ein, sondern std::vector erzeugt von dem übergebenen Objekt eine Kopie und legt diese im internen Puffer ab.
Was ich jetzt noch dazu sagen soll weiss ich auch nicht mehr, da fehlt wohl einiges an Grundlagen. Besorg dir mal ein C++ Buch, das sich auf den Sprachstandard beschränkt und nicht auf einen bestimmten Compiler abzielt.
-
das hab ich bereits, hab mir von Erlenkötter das C++ Buch besorgt und durchgearbeitet. Da wird aber leider über Vektoren gar nicht gesprochen. Hab auch vom Springer-Verlag das Buch "Die C++ Standardbibliothek". Ist aber was ich beim durchblättern gesehen hab nicht so einfach geschrieben.