Vektoren als Klassenattribute



  • Servus,

    wenn ich als Cpp-Newb das richtig verstanden habe, dann landen in meinem Header-File die Attribute der Klasse und in der cpp-Datei die Implementierung der Methodenrümpfe. Soweit so gut. Die Klassenattribute aus der h-Datei werden dann mittels Konstruktor bestückt. Ich bekomme es nicht hin, wie ich eine vector<string> mit Werten im Konstruktor initialisiere?

    Hier ein Codebrocken aus der cpp-Datei:
    #include <iostream>
    #include <string>
    #include <vector>
    #include "multiplesequencealignment.h"
    
    using namespace std;
    
    // Konstruktor, Wolf S. 286
    MultipleSequenceAlignment::MultipleSequenceAlignment(){
    
    	cout << "MultipleSequenceAlignment-Instanz erzeugt!\n";
    
    	string str( "Konstruktorstandardname" );
    	this->setName( str );
    
    	// TODO passende Laenge ermitteln Wolf S. 325
    	this->sequences = new vector<string>(4); // Dynamische Speicheranforderung  und Freigabe, Wolf S. 324
    	this->columnsStrings = new vector<string>(4);
    }
    
    // Destruktor
    MultipleSequenceAlignment::~MultipleSequenceAlignment(void){
    
    	delete sequences;
    	delete columnsStrings;
    	cout << "... Destruktor MultipleSequenceAlignment-Instanz aufgerufen!\n";
    }
    
    // Accessors
    string MultipleSequenceAlignment::getName(void){
    	return this->name;
    }
    void MultipleSequenceAlignment::setName(string namestr){
    	name = namestr;
    }
    
    vector<string> getSequences( void ){
    	return this->sequences;
    }
    
    void setSequences( vector<string> sequencesVector ){
    	this->sequences = sequencesVector;
    }
    

    Und hier ein Auszug aus der Header-Datei:

    class MultipleSequenceAlignment{
    private:
    	// Attribute
    	string name;
    	vector<string> sequences; // vector, Wolf S. 533f
    	vector<string> columnsStrings;
    
    	// private Methoden
    	// -
    
    public:
    	// Konstruktor Wolf S. 544
    	MultipleSequenceAlignment(); // Methodenrumpf hier, dann haben wir eine inline-Funktion
    
    	// Destruktor Wolf S. 544
    	~MultipleSequenceAlignment();
    
    	// Accessors
    	string getName(void);
    	void setName(string str);
    
    	vector<string> getSequences( void );
    	void setSequences( vector<string> sequences );
    
    	vector<string> getColumnsStrings( void );
    	void setColumnsStrings( vector<string> columnsStrings );
    
    	// Debugging
    	string toString( void ); // Instanzmethode, in Source-File gerumpft
    
    };
    

    Natürlich auch danke, wenn mir jemand sagt, was da bei meinen getter- und setter-Methoden nicht passt, in Eclipse hagelt es rote Kreuze, also das Zeichen für Fehler bei der Kompilierung.



  • Hallo,

    Also sequences und columnStrings sollten vom Typ vector<string>* sein (also Zeiger auf vector<string>) damit das mit der dynamischen Speicherallokation funtzt (new, delete). Aber noch viel besser wäre es natürlich, wenn die vectoren keine Zeiger wären - die vectoren auf dem Heap zu erzeugen macht hier nämlich gar keinen Sinn!

    Die getter/setter sollten eigentlich keine Errors produzieren, aber als Verbesserungsvorschlag könnte man sie auch so deklarieren:

    const string& getName() const;
    void setName(const string& name);
    //eventuell noch:
    string& getName();
    

    Stichwort "const correctness" und "call by reference", d.h. der inhalt des strings wird nicht jedesmal hin- und herkopiert sondern nur jeweils eine Referenz darauf.


  • Mod

    Oje, noch ein Opfer 🙄 . Das sind aber ganz schön viele heute. Was ich damit meine. Alternativen.

    Zu deiner eigentlichen Frage: Dynamsiche Speicherverwaltung ist hier völlig unangebracht. Was du brauchst ist eine Initialisierungsliste (mal nach googeln).



  • du deklarierst einen member sequences als std::vector<std::string> und weist dem im Konstruktor einen pointer auf std::vector<std::string> zu .. das kann nicht gehen. Vergiß new 🙂 und this-> ist auch seltenst nötig, am besten auch vergessen 😉

    class X 
    {
      std::vector<std::string> sequences;
    public:
      X();
    };
    
    X:X() 
    {
    // sequences ist kein pointer also nix new ... der ist schon fertig initialisiert. wenn du abschätzen kannst wieviele Strings du speichern willst kannste noch sequences.reserve(anzahl) machen ...
    sequences.reserve(20); // macht den so groß das 20 strings rein passen würden /(ist aus anderen Gründen hier dünnsinn aber egal)
    std::cout << sequences.size();
    sequences.push_back("ich"); std::cout << sequences.size() << std::endl;
    sequences.push_back("will"); std::cout << sequences.size() << std::endl;
    sequences.push_back("aber"); std::cout << sequences.size() << std::endl;
    sequences.push_back("nicht"); std::cout << sequences.size() << std::endl;
    sequences.push_back("!"); std::cout << sequences.size() << std::endl;
    for(int i=0; i<sequences.size(); ++i) std::cout << sequences[i] << "\t";
    std::cout << std::endl
    sequences.clear();std::cout << sequences.size() << std::endl;
    
    }
    

    Prizipiel::

    std::string   kp = "5";                      // kein pointer
     std::string * pp = new std::string("hallo"); // pointer
    
     std::cout << kp.size(); // member von "kein pointer" über .
     std::cout << pp->size(); // member von "pointer" über ->
    
     std::cout << (*pp).size(); // dereferenzierter pointer ... google ;)
    
     delete pp; // pointer muss man immer wieder aufräumen !
    


  • Danke für den vielen Input, ich habe da nun einiges drüber nachgedacht. Kann mir noch einer sagen, wie die getter- und setter bei den Attributen mit den vector-Listen heißen müssen?



  • Jay1980 schrieb:

    Danke für den vielen Input, ich habe da nun einiges drüber nachgedacht. Kann mir noch einer sagen, wie die getter- und setter bei den Attributen mit den vector-Listen heißen müssen?

    Welche getter und setter? Mach die nur, wenn es sinnvoll ist, und das ist es bei vector-Attributen eigentlich nie.



  • In dem Fall denke ich werde ich die sicher brauchen. Das Attribut im Header-File heisst vector<string> sequences aus der Klasse Msa, nun brauche ich die Methoden getSequences und setSequences ... wie müssen die aussehen? Das klappt bis jetzt noch nicht so.



  • Stelle dir mal folgende Frage: Was sind das denn für Sequenzen?

    Dann wirst du evtl. feststellen, das du keine Vectoren zurück geben mußt. Der Vector ist ein Implementierungs-Detail, welches man vor dem Anwender der Klasse verstecken kann bzw. sollte. Dadurch kannst du später z.B. auf eine Liste umsteigen (aus welchen Gründen auch immer), ohne das der Anwender seinen Code anpassen muß. Oder ihm eine ganz andere Sequenz in Sonderfällen zurück geben.

    Also, was ist der Sinn der Sequenzen? Welchen fachlichen Hintergrund haben sie?

    Einen vector<string> würde ich in einem setter/Getter nur als Convinience-Funktionen machen. Also unabhängig von der Implementierung.



  • Okay,
    ich überlege es mir später gern es anders zu machen, ist aber jetzt einer in der Lage und hat die Muße mir die setter- und getter-Methode einmal auszuformulieren.
    Es ist so: der Algorithmus, den ich in Java schon implementiert habe, läuft und da wird viel mit ArrayList<String> gearbeitet oder ArrayList<Sequence>, wenn ich die Liste mit einem der zahlreichen Hilfsobjekte bestücke - ich möchte also unbedingt wissen, wie man Accessormethoden für Listen in C++ erstellt.

    Zu den Sequenzen: es geht um ein Multiples Sequenz Alignment, ein Bündel von Strings, die zueinander ausgerichtet werden. Dann werden weitere Listen angelegt, etwa eine Liste aus den Strings pro Spalte. Es ist also wie eine Matrix. Ich kann aber vorher nicht wissen wieviel Sequenzen und wie lange sie sind, die Wahl eines Containers fand ich geeignet, in Java macht man das wohl auch so.



  • Vermutlich so:

    // 1.
    const std::vector<std::string> & getSequence() const { return &sequence; }  // nicht änderbare referenz auf deins
    // 2.
          std::vector<std::string>   getSequence()       { return  sequence; }  // kopie von deinem
    // 3.
    std::string sequences() // komma separierte liste von gefundenen Sequenzen
    {
      std::string erg;
    
      for(int i=0; i < sequence.size(); ++i) { erg.append(sequence[i]+","); }
    
      erg.resize(erg.size()-1); // delete last ','
    
      return erg;
    }
    

    Letzteres würde das Ergebnis unabhängig von der von dir verwendeten Datenhaltung machen ... änderst du std::vector->std::list wäre das für den Nutzer im 3. Fall egal, im 2. Fall könntest du deine Exportmethode so umschreiben das sie die Liste in nen Vektor stopft und den zurückgibt ... im ersten Fall würde der User seinen Code umbauen müssen (oder du musst richtig unsauber hacken, damit das noch tut).



  • Hier mal meine beiden Klassen:
    multisequencealignment.cpp

    #include <iostream>
    #include <string>
    #include <vector>
    #include "multiplesequencealignment.h"
    
    using namespace std;
    
    // Konstruktor, Wolf S. 286
    MultipleSequenceAlignment::MultipleSequenceAlignment(){
    
    	cout << "MultipleSequenceAlignment-Instanz erzeugt!\n";
    
    	string str( "Konstruktorstandardname" );
    	this->setName( str );
    
    	// TODO passende Laenge ermitteln Wolf S. 325
    	sequences.reserve(5);
    	columnsStrings.reserve(5);
    }
    
    // Destruktor
    MultipleSequenceAlignment::~MultipleSequenceAlignment(void){
    	cout << "... Destruktor MultipleSequenceAlignment-Instanz aufgerufen!\n";
    }
    
    // Accessors
    string& MultipleSequenceAlignment::getName(void){ // cbr durch Referenzen nachbilden, Wolf S. 184
    	return this->name;
    }
    void MultipleSequenceAlignment::setName(const string& name){
    	this->name = name;
    }
    
    vector<string> getSequences( void ){
    	return sequences;
    }
    
    void setSequences( vector<string> sequencesVector ){
    	this->sequences = sequencesVector;
    }
    
    // Debugging
    string MultipleSequenceAlignment::toString( void ){
    	string str;
    	str.append( "MulitpleSequenceAlignment-Instanz.toString: ");
    	str.append( "Name: " + name );
    	str.append( "\n" );
    	return str;
    }
    

    Und hier multisequencealignment.h:

    #ifndef MULTIPLESEQUENCEALIGNMENT_H_
    #define MULTIPLESEQUENCEALIGNMENT_H_
    
    #include <string>
    #include <vector>
    
    using namespace std;
    
    class MultipleSequenceAlignment{
    private:
    	// Attribute
    	string name;
    	vector<string> sequences; // vector, Wolf S. 533f
    	vector<string> columnsStrings;
    
    	// private Methoden
    	// -
    
    public:
    	// Konstruktor Wolf S. 544
    	MultipleSequenceAlignment(); // Methodenrumpf hier, dann haben wir eine inline-Funktion
    
    	// Destruktor Wolf S. 544
    	~MultipleSequenceAlignment();
    
    	// Accessors
    	string& getName(void);
    	void setName(const string& str);
    
    	vector<string> getSequences( void );
    	void setSequences( vector<string> sequences );
    
    	vector<string> getColumnsStrings( void );
    	void setColumnsStrings( vector<string> columnsStrings );
    
    	// Debugging
    	string toString( void ); // Instanzmethode, in Source-File gerumpft
    
    };
    
    #endif /* MULTIPLESEQUENCEALIGNMENT_H_ */
    

    In obiger Version im cpp-File erhalte ich Kompilierfehler, da 'sequences' not declared ist. Das verstehe ich nicht, denn bei name ging das ja auch, dass ich darauf zugreifen kann - was mache ich falsch, was habe ich übersehen?

    Das kostet mir bis jetzt viel Zeit und danke an alle, die sich immer noch überwinden können, mir mit dem ein oder anderen Codeschnippsel/Tipp zu helfen.

    PS.: ich will das unbedingt verstehen, in Java hatte ich auch mal so einen Zeitfresser, da war mir nicht klar, dass man Container in jedem Fall als Attribut initialisieren muss und bei normalen Variablen übernimmt das Java selbst - vielleicht gibt es ja hier auch so einen 'Unterschied'.



  • Du hast das MultipleSequenceAlignment:: vor den Funktionsnamen vergessen.



  • nimm mal das using namespace std; aus dem Header raus. Als generelle Regel, qualifizier in den .h's selbst, in der cpp kann man die using nutzen
    und this-> brauchst du in der cpp nicht ... durch das qualifizieren der Methoden (was du eben vergessen hast bei den settern) ist ein implizites this-> gegeben


Anmelden zum Antworten