Klassenentwurf



  • Hallo

    Grundsätzlich können auch Vektoren als normale Variablen übergeben werden. Dein Beispiel scheint also richtig zu sein. Ich empfehle dir aber, vectoren oder andere Container, die nicht lokal deklariert sind, immer als (konstante) Referenzen zu übergeben. Damit kann der Compiler verhindern, das unnötig kopiert wird.

    class TfrmImport
    {
      ...
      const std::vector<CDataset>& GetVDataset();
      CDataset VDataset;
    };
    
    const std::vector<CDataset>& TfrmImport::GetVDataset()
    {
        return VDataset;
    }
    

    Sollte VDataset doch nur eine im Getter lokale Variable sein, dann mußt du es wie bei dir als Kopie übergeben.

    bis bald
    akari



  • Nun habe ich in meiner Hauptform (frmMain) den Vektor als private-Member deklariert und die getMethode GetVDataset() geschrieben. Da die Daten in einer anderen Form (hier frmNewDataset) importiert werden muss ich in dieser Form über die get-Methode auf den vektor zugreifen. Hier mal das Ergebnis:

    void __fastcall TfrmNewDataset::btnCreateNewDatasetClick(TObject *Sender)
    {
       TStringList* stlPaths = new TStringList();
    
       try {
          //alle markierten Dateien in Stringliste schreiben:
          for (int i=0; i<lviewData->Items->Count; i++) {
             if (lviewData->Items->Item[i]->Checked)
                stlPaths->Add(lviewData->Items->Item[i]->Caption +  lviewData->Items->Item[i]->SubItems->Strings[0]); //hier werden die Pfade in die Stringliste geschrieben
          }
       }
       __finally {
          delete stlPaths;
       }
       //neuen Datensatz anlegen:
       frmMain->GetVDataset().back().ImportData(stlPaths);
       frmMain->GetVDataset().push_back(CDataset());
    
       Close();
    }
    

    Soweit ich das verstanden habe wird dadurch der Konstruktor der Klasse CDataset aufgerufen und das Objekt in den Vektor geschoben. Nun möchte ich auf die Methode ImportData zugreifen um VData zu füllen. Das muss ich ja vor dem push_back machen oder? Also muss ich ja auf das letzte Element mit GetVDataset().back() zugreifen. Kann ich das so machen?
    Leider funktioniert bei solchen Sachen die Code-Vervollständigung vom Builder nicht.

    Die Methode GetVDataset konnt ich nicht als const deklarieren. Dann gibt es die Warnung, das push_back keine const-Funktion ist.



  • Hallo

    Ja, das const must du in deinem jetzt geschildertem Fall weglassen. Du brauchst eine Referenz die du bearbeiten kannst. Sei aber froh das ich dich generell auf die Referenz hingewiesen habe, denn mit deinem vorherigen Kopier-Getter würdest du auch nicht weit kommen...

    Zum Problem : Wenn schon, dann must du natürlich zuerst das Dataset anlegen und danach die Import-Funktion anwenden :

    frmMain->GetVDataset().push_back(CDataset()); // Neues Dataset am Ende anfügen
       frmMain->GetVDataset().back().ImportData(stlPaths); // Dataset am Ende mit Daten füllen
    

    bis bald
    akari



  • Nebenbei bemerkt, ich würde diesen try _finally Kram hier eher nicht machen.
    Ein try ist hier nicht nötig da bei korrekter Anwendung der StringList keine exception fliegen kann.
    Ich würde die StringList eher in einen auto_ptr packen. Der kümmert sich dann um das löschen.

    void __fastcall TfrmNewDataset::btnCreateNewDatasetClick(TObject *Sender)
    {
       std::auto_ptr<TStringList> stlPaths(new TStringList);
    
       //alle markierten Dateien in Stringliste schreiben:
       for (int i=0; i<lviewData->Items->Count; i++) {
          TListItem* item = lviewData->Items->Item[i]; // nur zum Sparen von Schreibarbeit
          if (item->Checked)
             stlPaths->Add(item->Caption +  item->SubItems->Strings[0]); //hier werden die Pfade in die Stringliste geschrieben
       }
       //neuen Datensatz anlegen:
       frmMain->GetVDataset().back().ImportData(stlPaths);
       frmMain->GetVDataset().push_back(CDataset());
    
       Close();
    }
    


  • ich habe Deinen Tipp berücksichtigt und das Programm umgeschrieben. Leider erhalte ich dann folgende
    Fehler in dieser Zeile:

    frmMain->GetVDataset().back().ImportData(stlPaths);
    

    Fehlermeldungen:

    [C++ Fehler] unitNewDataset.cpp(234): E2034 Konvertierung von 'std::auto_ptr<TStringList> *' nach 'TStringList *' nicht möglich
    [C++ Fehler] unitNewDataset.cpp(234): E2342 Keine Übereinstimmung des Typs beim Parameter 'stlPaths_' ('TStringList *' erwartet, 'std::auto_ptr<TStringList> *' erhalten)
    

    Dann kann ich den auto-ptr auch in dieser methode umschreiben?

    void CDataset::ImportData(TStringList* stlPaths_)
    {
       for (int i=0; i<stlPaths_->Capacity; i++) { //durchläuft alle Dateien in der Stringliste
          TStringList* stlData = new TStringList();
          stlData->LoadFromFile(stlPaths_->Strings[i]);
    
          try {
             for (int j=0; i<stlData->Count; i++) {
                TDateTime temp_timestamp;
                double temp_value;
                SplitString(stlData->Strings[j], &temp_timestamp, &temp_value);
                VData[i][j].value = temp_value;
                VData[i][j].timestamp = temp_timestamp;
                VData.push_back(std::vector<SValue>()); // erste Ebene füllen
                VData[j].push_back(SValue()); // zweite Ebene füllen
             }
          }
          __finally {
             delete stlData;
          }
       }
    }
    

    das mit dem try finally hatte ich in einem Artikel zu TStringList gelesen: http://www.fachinformatiker-ihk.de/download/extras/Fh_mit_BCB.pdf



  • Hallo

    rudpower schrieb:

    ich habe Deinen Tipp berücksichtigt und das Programm umgeschrieben. Leider erhalte ich dann folgende
    Fehler in dieser Zeile:

    frmMain->GetVDataset().back().ImportData(stlPaths);
    

    Las den auto_ptr noch seinen Inhalt als normalen Zeiger zurückgeben

    frmMain->GetVDataset().back().ImportData(stlPaths.get());
    

    Dann kann ich den auto-ptr auch in dieser methode umschreibe
    n?

    Ja, auch dafür kannst du auto_ptr benutzen und __finally entfernen.
    Überhaupt kannst du auto_ptr immer dann benutzen, wenn du eine rein lokale Instanz hast, die du eigentlich statisch auf dem Stack anlegen würdest (ohne new), aber es wegen der VCL-Konventionen nicht machen kannst.

    das mit dem try finally hatte ich in einem Artikel zu TStringList gelesen

    Grundsätzlich ist __finally ja eine nette Erweiterung im Builder - für C. In C++ sollte man lieber RAII benutzen.

    bis bald
    akari



  • akari schrieb:

    Hallo

    Burkhi schrieb:

    ..noch ergänzend dazu:
    In structs sind alle Members standardmässig public, in classes private.

    Das habe ich in meinem Post auf Seite 1 schon geschrieben, das von dir zitierte ist nur die Wiederholung.

    bis bald
    akari

    Oh Pardon, hab ich glatt übersehen 😉



  • ok danke für den Tipp.
    Die Kompilierung läuft durch. Nachdem ich meine Dateien ausgewählt habe und den Datensatz anlege bricht das Programm mit einer EAccessViolation ab. Wenn ich den Haltepunkt in die ImportData-Methode setze kommt der Programmabbruch in der Zeile "VData[i][j].value = temp_value;"

    void CDataset::ImportData(TStringList* stlPaths_)
    {
       for (int i=0; i<stlPaths_->Count; i++) { //hier war noch ein Fehler, da stand vorher stlPaths_->Capicity
          std::auto_ptr<TStringList> stlData(new TStringList);
          stlData->LoadFromFile(stlPaths_->Strings[i]);
    
          for (int j=0; i<stlData->Count; i++) {
             TDateTime temp_timestamp;
             double temp_value;
             SplitString(stlData->Strings[j], &temp_timestamp, &temp_value);
             VData[i][j].value = temp_value; //hier bricht das Programm ab!
             VData[i][j].timestamp = temp_timestamp;
             VData.push_back(std::vector<SValue>()); // erste Ebene füllen
             VData[j].push_back(SValue()); // zweite Ebene füllen
          }
       }
    }
    


  • Hallo

    Ich habe nicht den Eindruck, das du überhaupt verstehst, was du programmierst...
    Denn sonst würde dir auffallen, das du zuerst Daten in den vector schiebst, und erst danach den Platz im vector schaffst, wo die Daten eigentlich hinsollen.

    bis bald
    akari



  • Ich stimme akari voll zu und würde dir (rudpower) raten dich mal über vector etwas gründlicher schlau zu machen.
    In guter Einstieg dafür bietet das hiesige Magazin. Z. Bsp. hier
    http://www.c-plusplus.net/forum/143816
    In diesen Fall hier könnte man auf push_back sogar ganz verzichten und std::vector::resize bemühen. Immerhin steht die Größe des vectors ja schon vorher fest.



  • so, nun hab ich mich nochmal genau mit vector beschäftigt. War klar, dass das nicht funktioniert wenn ich auf Elemente zugreife, die noch nicht reserviert wurden. Hier nun der funktionierende Code:

    void CDataset::ImportData(TStringList* stlPaths_)
    {
       VData.resize(stlPaths_->Count);
       for (int i=0; i<stlPaths_->Count; i++) { //durchläuft alle Dateien in der Stringliste
          std::auto_ptr<TStringList> stlData(new TStringList);
          stlData->LoadFromFile(stlPaths_->Strings[i]);
    
          for (int j=0; j<stlData->Count; j++) {
             TDateTime temp_timestamp;
             double temp_value;
             SplitString(stlData->Strings[j], &temp_timestamp, &temp_value);
    
             VData[i].resize(stlData->Count);
    
             VData[i][j].value = temp_value;
             VData[i][j].timestamp = temp_timestamp;
          }
       }
    }
    

    Vielen Dank nochmal für die Hilfestellungen und ein schönes Wochenende.



  • Da ist noch ein Fehler drin.
    Das zweite resize muss eine Schleife nach oben verschoben werden sonst machst du es viel zu oft.



  • Burkhi schrieb:

    akari schrieb:

    Hallo

    Burkhi schrieb:

    ..noch ergänzend dazu:
    In structs sind alle Members standardmässig public, in classes private.

    Das habe ich in meinem Post auf Seite 1 schon geschrieben, das von dir zitierte ist nur die Wiederholung.

    bis bald
    akari

    Oh Pardon, hab ich glatt übersehen 😉

    Sorry für den Spam, aber ich bitte Burkhi mich über Private Nachricht anzuschreiben, ist wichtig. Oder geht das in diesem Forum nicht?



  • Hallo

    Reptilex schrieb:

    Sorry für den Spam, aber ich bitte Burkhi mich über Private Nachricht anzuschreiben, ist wichtig. Oder geht das in diesem Forum nicht?

    Die privaten Nachrichten sind in diesem Forum deaktiviert.
    Burkhi hat in seinem Profil auch keine E-Mail Adresse hinterlegt. Also kannst du Burkhi bitten, das er dir eine E-Mail schickt, an die Adresse in deinem Profil.

    bis bald
    akari



  • akari schrieb:

    Hallo

    Reptilex schrieb:

    Sorry für den Spam, aber ich bitte Burkhi mich über Private Nachricht anzuschreiben, ist wichtig. Oder geht das in diesem Forum nicht?

    Die privaten Nachrichten sind in diesem Forum deaktiviert.
    Burkhi hat in seinem Profil auch keine E-Mail Adresse hinterlegt. Also kannst du Burkhi bitten, das er dir eine E-Mail schickt, an die Adresse in deinem Profil.

    bis bald
    akari

    Hallo,

    okay dann mach ich das hiermit:

    Burkhi, bitte schreibe mir eine Mail an meine E-Mail damit ich dir dort schreiben kann!

    Schade, dass man ihn nicht leichter kontaktieren kann 😞



  • Soweit funktioniert es: Leider gibt es beim Schließen des Programms eine Exception:

    Im Project1.exe ist eine Exception der Klasse EAccessViolation augetreten. Meldung: 'Zugriffsverletzung bei Adresse 004026DE im Modul 'Project1.exe'. Lesen von Adresse 00000064'. Prozess wurde angehalten. 
    Mit Einzelne Anweisung oder Start fortsetzen.
    

    Ich vermute, dass irgendwo Speicherlecks entstehen beim Schließen der Anwendung.
    Nach dem Schließen der Anwendung bleibt er im Code an der Stelle for (int i=0; i<count; i++) stehen. Hier scheint irgendwo ein Fehler zu sein.

    void __fastcall TfrmMain::lviewDatasetSelectItem(TObject *Sender,
          TListItem *Item, bool Selected)
    {
       int index = Item->Checked; //welcher Datensatz ausgewählt
       int count = VDataset[index].GetVFilenames().size();
       for (int i=0; i<count; i++) {
          lboxData->Items->Add(VDataset[index].GetVFilenames()[i]);
          TLineSeries *serImportedData = new TLineSeries(Chart1);
          serImportedData->ParentChart = Chart1;
          for (unsigned int j=0; j<VDataset[index].VData[i].size(); j++){}
             serImportedData->AddXY(VDataset[index].VData[i][j].timestamp, VDataset[index].VData[i][j].value, "", clBlue);
             serImportedData->AddY(VDataset[index].VData[i][j].value, "", clBlue);
    
       }
    }
    

    Vielleicht liegt der Fehler auch beim Anlegen des Datensatzes. Der Vektor Datensatz ist ja in private der frmMain (das ist bei mir die erste Form) deklariert. Dazu gibt es eine Schnittstelle:

    std::vector<CDataset> &TfrmMain::GetVDataset()
    {
       return VDataset;
    }
    

    In einer zweiten Form wird der Datensatz über einen Button angelegt:

    void __fastcall TfrmNewDataset::btnCreateNewDatasetClick(TObject *Sender)
    {
       std::auto_ptr<TStringList> stlPaths(new TStringList);
    
       //alle markierten Dateien in Stringliste schreiben:
       for (int i=0; i<lviewData->Items->Count; i++) {
          TListItem* item = lviewData->Items->Item[i];
          if (item->Checked)
             stlPaths->Add(lviewData->Items->Item[i]->Caption +  lviewData->Items->Item[i]->SubItems->Strings[0]);
       }
       //neuen Datensatz anlegen und Messdaten importieren:
       frmMain->GetVDataset().push_back(CDataset());
       frmMain->GetVDataset().back().ImportData(stlPaths.get());
       frmMain->GetVDataset().back().SetPlantOperator(cmbLocation->Text);
       frmMain->GetVDataset().back().SetIdentifier(txtIdentifier->Text);
    
       Close();
    }
    

    Vielleicht ist es besser den Vektor in der zweiten Form zu deklarieren und dort eine Schnittstellenmethode zu schreiben?


Anmelden zum Antworten