Merkwürdige Compilermeldung wegen Konstruktor

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)

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

**Aus File.h**

private:
char* path;
struct stat attributes;

public:
/** The system-dependent path-separator character, represented as a string for convenience. /
//static const string pathSeparator = „/“;
/
* The system-dependent path-separator character. /
static const char pathSeparatorChar = ‚/‘;
/
* The system-dependent default name-separator character, represented as a string for convenience. /
//static const string separator = „/“;
/
* The system-dependent default name-separator character. */
static const char separatorChar = ‚/‘;

File( char* );
~File();```

ich denk eher du hast einen Fehler in der Datei, zeig uns mal den gesamten Code

Ich hab ihn mittlerweile selbst gefunden. Ich wusste nicht dass man im Headerfile ein Semikolon nach den Bockklammern der Klassendefinition machen muss. 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 …

	char** files;
	
	DIR *dir;
	struct dirent *dirp;
	if ( this->isDirectory() && (dir=opendir(this->path)) != 0 ) {
		int filecounter = 0;

		while ( (dirp=readdir(dir)) != 0 ) {
			if ( strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..") )
				filecounter++;
		}
		rewinddir( dir );
		files = new char[filecounter*256];
		
		std::cout << "Dateianzahl: " << filecounter << "." << std::endl;

		int i = 0;
		while ( (dirp=readdir(dir)) != 0 ) {
			files** = dirp->d_name;
			/*if ( strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..") ) {
				std::cout << i << ": " << dirp->d_name << std::endl;
				i++;
			}*/
		}

		closedir( dir );
	} else {
		std::cerr << "Cannot read directory!" << std::endl;
	}

	return files;
}```

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

char** files;
...
files = new char[filecounter*256];

Beschwert er sich da nicht beim compilieren?

char **files ist (etwas unpräzise)

  • ein Pointer auf einen Pointer oder
  • ein Array von Pointern oder
  • ein Array von Arrays
    (zu meinen aktiven C+±Zeiten war ich da mit den Begriffen etwas pedantischer, aber vielleicht hilft ja eine der Beschreibungen zu intuitiverem Verständnis :wink: ). Man muss erst Platz für die Pointer reservieren - danach kann man jeden einzelnen initiialisieren.

Code sagt mehr als 1000 Worte


char **file;
file = new char*[fileCounter]; // Platz für 'fileCounter' Zeiger
for (int i=0; i<fileCounter; i++)
{
    file** = new char[256]; // Jeder Zeiger zeigt auf 256 bytes
}

Beim Löschen das ganze dann in Umgekehrter Reihenfolge…


char **file;
for (int i=0; i<fileCounter; i++)
{
    delete[] file**; // Lösche die 256 bytes
}
delete[] file; // Lösche den Platz für 'fileCounter' Zeiger

Ach, noch so als Nachtrag:
files** = dirp->d_name;
damit würden die 256 allokierten bytes aber vmtl. im Nirvana verschwinden (->Speicherleck!). Du musst entweder das, was in dirp->d_name steht, in den 256 bytes großen Array namens “files**” reinkopieren, oder NUR die ‘fileCounter’ pointer erstellen. Die Frage, wer dann für das Freigeben von dirp->d_name (also dann von files**) verantwortlich ist, muss aber geklärt sein…

Wenn Du mit C++ arbeitest dann tu das auch und mische kein C mit rein. In C++ gibt es die STL diese bietet Dir einen std::vector< T > welcher ein dynamisches Array wrapped. Nutze diesen! Ebenso haben c-strings nix in C++ zu suchen, dafür gibt es std::string/std::wstring. Wenn Du die std-lib von C++ und die STL verwendest kommt es erst gar nicht zu solchen Problemen!

Zu dem Fehler mit dem Semikolon: Du meinst sicherlich das Semikolon nach der Klassendefinition?

struct c
{ }; // <-- das?

Das ist normal in C++, genauso wie der Include-Guard. Klar, das sind Dinge wo man denkt, das sowas ein Compiler eigentlich hinbekommen sollte, aber anscheinend wurde dies damals beim verabschieden des Standards „vergessen“ oder es gibt andere Gründe dafür.
Auch aufpassen musst Du beim verwenden von Templates:

template<typename T>
struct good
{ T* m; };

template<template<typename T>>  // >> geht nicht da der Compiler hier vom shift-operator ausgeht.
struct bad
{ T* m; };

Ich mache daher immer Leerzeichen vor und nach die eckigen Klammern bei templates.

Gut Schuß
VuuRWerK :wink:

Danke für deine ausführliche Antwort :slight_smile:
Sie wird mir sicherlich noch einmal nützlich sein, aber ich habe mich aufgrund der Empfehlung eines anderen C+±Programmierers für list entschieden. Das ist für mich als Java-Programmierer zwar noch immer etwas umständlich (und auch undurchschaubar, ich finde die Syntax schrecklich), aber immer noch besser als im C-Stil ^^

Das Programm läuft jetzt soweit, nur habe ich ein Problem beim freigeben des Speichers. Das Compilieren erzeugt keine Fehler- oder Warnmeldungen, doch sobald ich das Programm ausführe bricht es ab mit der Meldung:

free(): invalid pointer 0x…

Mein Destruktor: File::~File() { delete path; delete &attributes; }

Daneben noch eine kleine Verständnisfrage: In der string.h gibt es ja keine Funktion substr(), daher habe ich getName() so umgesetzt: const char* File::getName() { return strrchr(this->path, separatorChar)+1; }
Ist das so in Ordnung, oder entsteht dadurch ein Memory Leak? Muss ich den Speicher für den Dateinamen nach dem Aufrufen manuell wieder freigeben?
Und wie ist das eigentlich mit den Templates? Ich weiß nichtmal wozu die gut sind und ich hab gelesen dass einige Compiler Templates aufgrund ihrer komplexen Implementierung gar nicht drauf haben.

EDIT: @VuuRWerK
Ich habe deinen Beitrag erst jetzt bemerkt. Ist es wirklich so, dass char*-Strings in C++ verpöhnt sind? Ich habe diese immer wieder in C+±Tutorials im Internet gefunden und dachte ich ziehe diese aus Performancegründen vor.

Die sind in der Tat im ISO-C++ verpöhnt, jedoch brauchst Du keine Angst haben das Du jedesmal ein string-objekt erzeugen musst damit es geht sondern der Compiler nimmt eine implizite Konvertierung vor da std::string einen Konstruktur für einen char*-Zeiger implementiert.

void f(std::string const& str)
{ }

int main(int argc, char* argv[])
{
  f("striiing");   // ok, der compiler macht daraus f(std::string("striiing"));
  return 0;
}

Mit std::string hast Du einfach keine Probleme die Du mit C-Strings hast. Und mach Dir über Performance keine Sorgen, 1. optimiert der Compiler besser als wenn Du C-Strings verwendest, 2. C ist ein subset von C++ und somit ist C++ fast genauso schnell wie C :wink:

std::list< T > ist übrigens eine verkettete Liste, also kein index-basierter Zugriff!

Gut Schuß
VuuRWerK :wink:

Ah, ich verstehe!
Wäre demnach Vector besser geeignet?

Sofern Du auf den index-basierten zugriff bestehst ja. Solltest Du nur eine liste haben wollen da Du eine Liste brauchst zum durch iterieren dann kannst Du auch std::list< T > verwenden. Ansonsten, um Dir eine wirklich praktische Entscheidungshilfe zu nenen, sei Dir dieses Diagramm ans Herz gelegt!

Gut Schuß
VuuRWerK :wink: