Merkwürdige Compilermeldung wegen Konstruktor (C++)

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    • Merkwürdige Compilermeldung wegen Konstruktor (C++)

      Ich probiere gerade mit C++ anzufangen und probiere gerade die Java-Klasse File nachzuprogrammieren. Dabei scheitere ich anscheinend schon bei der Objektorientierung:

      Beim Compilieren (g++ Version 4.3.3) bekomme ich immer diese Fehlermeldung:
      /File.cpp:23: Fehler: neue Typen dürfen nicht in einem Rückgabetyp definiert werden
      /File.cpp:23: Anmerkung: (vielleicht fehlt ein Semikolon hinter der Definition von »File«)
      /File.cpp:23: Fehler: Angabe des Rückgabetyps für Konstruktor ist ungültig


      Aus File.cpp (Zeile 23 = Zeile 1)

      Quellcode

      1. File::File( char* name ) {
      2. this->path = name;
      3. if ( stat((const char*) &path, &attributes) == -1 ) {
      4. std::cerr << "Couldn't read attributes of file '" << name << "'!" << std::endl;
      5. }
      6. }


      Aus File.h

      Quellcode

      1. private:
      2. char* path;
      3. struct stat attributes;
      4. public:
      5. /** The system-dependent path-separator character, represented as a string for convenience. */
      6. //static const string pathSeparator = "/";
      7. /** The system-dependent path-separator character. */
      8. static const char pathSeparatorChar = '/';
      9. /** The system-dependent default name-separator character, represented as a string for convenience. */
      10. //static const string separator = "/";
      11. /** The system-dependent default name-separator character. */
      12. static const char separatorChar = '/';
      13. File( char* );
      14. ~File();
      Alles anzeigen


      Nachdem es hier im Forum ein paar C++-Cheggas gibt, hoffe ich dass mir vlt. jemand helfen kann ^^

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Christoph ()

    • Hi,

      so beim drüberschauen hab ich zwar nicht den Fehler entdeckt, aber du machst da im Konstruktor einen sehr seltsamen Cast:

      stat((const char*) &path, &attributes)

      path ist char* und wenn du die Adresse davon nimmst bekommst du char ** und diese castest du nach (const char*). Wenn du schon casten musst, dann mach (const char*) path.

      Poste mal den gesamten Source, vllt. kann man dir dann besser helfen. Wenn ich mal raten müsste würde ich mal sagen, dass eventuell das Semikolon nach der Klassen-Deklaration fehlt:

      Quellcode

      1. class Something {
      2. // ...
      3. }; // <- das hier nicht vergessen


      Das führt nämlich desöfteren zu seltsamen Fehlern.
    • Also einen Fehler direkt habe ich jetzt auch nicht gefunden, aber wie Nax schon gesagt hat, das Semikolon nach der geschwungen Klammer hat mich auch schon mal zum Wahnsinn getrieben

      Aber @Nax:
      Es dürfte doch reichen stat(path, &attributes) zu schreiben, oder? Da path ja ein Char-Array ist und Arrays in C++ so wie so immer als Referenz übergeben werden. Oder liege ich da falsch? Hab schon so lange kein C++ mehr programmiert.

      Und wenn ich falsch liege, und du auf const char casten willst, damit die Variable in der Funktion nicht verändert wird, reicht es auch, da dann eh Call by Value ist und da wird der Wert von path eh nicht verändert..
      James
      [Blockierte Grafik: http://dl.dropbox.com/u/24753690/stuff/forenlinks/rocketsign.png]
      Team Rocket - so schnell wie das Licht,
      gebt lieber auf und bekämpft uns nicht!

      join #teamrocket @iz-smart.net

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Stan ()

    • Grundsätzlich ist der const-Cast überflüssig, da dieser sowieso implizit gemacht wird und eigentlich nur ein Constraint für die stat-Funktion ist, die die char-Elemente im Speicher theoretisch dadurch eben nicht ändern darf, was natürlich im Falle von stat auch großen Sinn macht. Praktisch kann man natürlich bei C(++) das const ganz leicht wegcasten, da die Sprache(n) schwach typisiert sind und die Werte im Speicher auf denen der Pointer zeigt lustig verändern. Je nachdem wohin der zeigt, gibt's dann entweder nur eine böse Überraschung oder eine Segfault.

      Da path ja ein Char-Array ist und Arrays in C++ so wie so immer als Referenz übergeben werden. Oder liege ich da falsch? Hab schon so lange kein C++ mehr programmiert.

      Naja, es wird eigentlich der Pointer auf den Stack der Funktion kopiert. Man kann den Pointer selbst in der Funktion dadurch nicht irgendwie für den Caller umbiegen, aber sehr wohl den Speicherbereich verändern worauf er zeigt. Wäre es eine Referenz so könnte man den Pointer direkt umbiegen und in zB für den Caller auf NULL setzen.
    • Ich hab den Fehler mittlerweile selbst gefunden. Ich wusste nicht dass man im Headerfile ein Semikolon nach den Bockklammern der Klassendefinition machen muss. Den const-Cast hab ich entfernt. Der Dateipfad wird jetzt von vornherein als const char* abgespeichert. Es funktioniert soweit jetzt auch, nur habe ich ein anderes Problem bei der Implementierung von list(). Ich kann in C++ einfach noch nicht ordentlich mit den Arrays und Pointern umgehen ...

      Quellcode

      1. char** File::list() {
      2. char** files;
      3. DIR *dir;
      4. struct dirent *dirp;
      5. if ( this->isDirectory() && (dir=opendir(this->path)) != 0 ) {
      6. int filecounter = 0;
      7. while ( (dirp=readdir(dir)) != 0 ) {
      8. if ( strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..") )
      9. filecounter++;
      10. }
      11. rewinddir( dir );
      12. files = new char[filecounter*256];
      13. std::cout << "Dateianzahl: " << filecounter << "." << std::endl;
      14. int i = 0;
      15. while ( (dirp=readdir(dir)) != 0 ) {
      16. files[i] = dirp->d_name;
      17. /*if ( strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..") ) {
      18. std::cout << i << ": " << dirp->d_name << std::endl;
      19. i++;
      20. }*/
      21. }
      22. closedir( dir );
      23. } else {
      24. std::cerr << "Cannot read directory!" << std::endl;
      25. }
      26. return files;
      27. }
      Alles anzeigen


      Ich krieg es einfach nicht gebacken ein String-Array zu bauen.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Christoph ()

    • Andere Frage: Warum ärgerst du dich eigentlich mit char* und char** herum, wenn es std::string und std::list dafür gibt? Das ist mehr C++ish als char**.

      Ansonsten probier mal so etwas in der Art:

      Quellcode

      1. char **lines = NULL;
      2. lines = new char*[10];
      3. lines[0] = new char[10];
      4. lines[0][0] = 'a';
      5. lines[0][1] = 'b';
      6. lines[0][2] = '\0';
      7. lines[1] = new char[10];
      8. lines[1][0] = 'c';
      9. lines[1][1] = 'd';
      10. lines[1][2] = '\0';
      11. cout << lines[0] << endl;
      12. cout << lines[1] << endl;
      13. delete []lines[0];
      14. delete []lines[1];
      15. delete []lines;
      Alles anzeigen
    • Gut, ich hab's nur zuerst anders versucht weil ich dachte das wäre performanter. Erstmal musste ich die Methode von list() in ls() umbenennen weil sonst der Compiler meckert. Ich krieg es allerdings nicht ganz auf die Reihe die Liste zu füllen ...

      Quellcode

      1. list<string> File::ls() {
      2. list<string> files;
      3. DIR *dir;
      4. struct dirent *dirp;
      5. if ( this->isDirectory() && (dir=opendir(this->path)) != 0 ) {
      6. while ( (dirp=readdir(dir)) != 0 ) {
      7. if ( strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..") )
      8. files->push_back( new string(dirp->d_name) );
      9. }
      10. closedir( dir );
      11. } else {
      12. std::cerr << "Cannot read directory!" << std::endl;
      13. }
      14. return files;
      15. }
      Alles anzeigen
    • `new string` erzeugt einen Pointer auf ein String-Objekt, du brauchst aber direkt ein String-Objekt, also:

      Quellcode

      1. files.push_back(string(dirp->d_name) );

      Außerdem ist files auch kein Pointer auf eine Liste, sondern direkt ein list-Objekt. Daher musst du mit `.` arbeiten anstatt mit `->`.
    • Very well, dankeschön :) Ich bin Sachen Programmierung ziemlich an Java gewöhnt, da ist C++ doch ziemlich anders ^^

      Ich habe jetzt nur noch ein kleines Problem bezüglich dem Destruktor. Beim Ausführen steht immer was von einem "Invalid pointer".

      Quellcode

      1. File::~File() {
      2. delete this->path;
      3. delete &this->attributes;
      4. }

      Er hat anscheinend nur ein Problem beim freigeben von der Struktur "attributes" (vom Typ stat).
    • Du musst attributes nicht freigeben, da es kein Pointer ist sondern direkt ein Objekt, das in der Klasse gespeichert wird.

      Mit `stat(..., &attributes)` übergibst du der stat-Funktion als 2. Parameter einen Pointer zu dieser Struktur (der `&`-Operator liefert sozusagen die Adresse vom Objekt zurück worauf er angewandt wird). Die Funktion befüllt damit die Struktur auf die der Pointer zeigt, also deine attributes stat-Struktur in der Klasse selbst.
    • Warum willst du denn den Pfadnamen freigeben? Soviel ich gesehn habe legst du doch den String nicht mit dem Schlüsselwort new an.. Oder bin ich blind?

      Edit:

      Wegen String Array wenn du nicht die Klasse String verwenden willst

      Nimm halt char* array[30];
      James
      [Blockierte Grafik: http://dl.dropbox.com/u/24753690/stuff/forenlinks/rocketsign.png]
      Team Rocket - so schnell wie das Licht,
      gebt lieber auf und bekämpft uns nicht!

      join #teamrocket @iz-smart.net

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Stan ()

    • Das Problem ist, dass du lediglich einen Pointer auf einen Speicherbereich im Konstruktor kopierst, nicht allerdings die Speicherinhalte. Normalerweise müsstest du im Konstruktor einen neuen Speicherbereich für den Pfad allokieren und dann halt Zeichen für Zeichen die Inhalte rüberkopieren. Danach kannst du den Pfad im Destruktor wieder freigeben. In deinem Fall löscht du möglicherweise Speicherbereich der eigentlich dem Caller "gehört".

      Du kannst natürlich zur Speicherung des Pfades auch ganz einfach std::string verwenden, das genau das in diesem Fall für dich erledigt.

      In deinem Fall hast du wahrscheinlich die Klasse mit File("testfile") getestet. Da der String "testfile" aber im statischen Speicherbereich liegt führt eine Freigabe des Speichers in dem Fall zu einer segfault.
    • Original von Christoph
      Heißt das, das ich in C/C++ ausschließlich Pointer freigeben muss?

      Das kommt auch auf die API an die du verwendest. FILE*-Pointer musst du zB mit fclose "freigeben". Grundsätzlich gilt aber: Wenn du einen Pointer hast und für diesen Speicher mit new/malloc allokiert hast musst du ihn mit delete/free wieder freigeben, wobei du malloc, new, delete und free nicht mischen darfst. Also Speicher der mit malloc allokiert wurde muss auch mit free wieder freigegeben werden und Speicher, der mit new gelöscht wurde, muss mit delete wieder freigegeben werden.

      Dadurch musst du dich dann bei C(++) auch Gedanken darum machen welches Objekt genau welche Ressource wann wie freigibt, was oftmals gar nicht so einfach ist. Im einfachsten Falle hat man das Problem in deinem Beispiel: der Caller übergibt einen Char-Pointer dem Konstruktor. Kopierst du nun den Inhalt vom Char-Pointer und gibst ihn dann mit dem Destruktor wieder frei oder behältst du dir einfach nur den Pointer auf den Speicherbereich und hoffst dass der Caller den Speicherbereich nicht von außen modifiziert. "Lustiger" wird es dann wenn man zyklische Referenzen hat und ggf. auch noch ein paar Workerthreads hat, die irgendwie beendet werden müssen aber blockend auf eine Ressource warten, ...

      Wenn du einfach nur ein Objekt hast, also keinen Pointer, dann kannst du das Objekt nicht mit free/delete freigeben. Das Objekt wird automatisch zerstört wenn es entweder auf dem Stack liegt und der Scope in dem es angelegt wurde verlassen wird, oder wenn es von einem anderen Objekt aggregiert wird und dieses dann freigegeben wird (entweder wiederum auf dem Stack, oder aber auf dem Heap via delete).