Forum: PC-Programmierung [C++] Klasse von std::FILE ableiten


von In the Dessert I can't remember my Name (Gast)


Lesenswert?

Gentle(wo)man,

beim C++ üben habe ich die Macht der Ableitung erfahren. Jetzt versuche 
ich gerade eine Klasse für Dateien zu schreiben. Da dachte ich, ich 
leite einfach von std::FILE ab.
1
 
2
#include <cstdio>
3
#include <string>
4
5
using namespace std;
6
7
class Datei: public FILE {
8
public:
9
        ~Datei() {
10
                fclose(this);
11
        }
12
        Datei(string s) {
13
                *this = *static_cast<Datei*>(fopen(s.c_str(), "w"));
14
        }
15
};
16
17
int main() {
18
        Datei d("hallo.txt");
19
        fprintf(&d, "Hallo Welt!");
20
        return 0;
21
}

Leider bekomme ich zur Laufzeit einen Fehler (nur mit GNU C++)
1
Fatal error: glibc detected an invalid stdio handle

Mit OpenWatcom 1.9 compiliert wird die Datei zwar ohne Fehlermeldung 
erstellt, bleibt aber leer.

Wie gehe ich das richtig an, so dass ich mit einer von std::FILE 
abgeleiteten Klasse arbeiten kann, bzw. wie initialisiert man das ganze?

von Dirk K. (merciless)


Lesenswert?

In the Dessert I can't remember my Name schrieb:
> Wie gehe ich das richtig an

Lass es bleiben, ist kein guter Stil.
Das ist eine bessere Lösung:
https://de.wikipedia.org/wiki/Komposition_an_Stelle_von_Vererbung

Und ein gutes Buch über C++ würde dir auch helfen,
Konstruktoren und den this-Pointer zu verstehen.

merciless

von In the Dessert I can't remember my Name (Gast)


Lesenswert?

Dirk K. schrieb:
> ein gutes Buch über C++ würde dir auch helfen

Danke für Dein schonungsloses Feedback. Welches Buch kannst Du 
empfehlen?

von Gelegenheitscoder (Gast)


Lesenswert?

glibc will dir sagen, dass du einen Pointer auf eine FILE-Instanz an die 
stdio-Funktionen übergeben hast, die nicht von der glibc erzeugt wurde.

std::FILE ist keine richtige C++-Klasse, sondern nur ein zusätzlicher 
Name für die normale FILE-struct aus der stdio.h von C. Davon eine 
Klasse abzuleiten mag zwar zufälligerweise gehen, ist aber 
typischerweise nicht sinnvoll.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Du kannst eine FILE-Struktur nicht einfach kopieren und hoffen, dass
fprintf, fclose usw. auch auf dieser Kopie funktionieren. In der
Standardbibliothek wird über die offenen Dateien Buch geführt, wobei
jede dieser Dateien wohl durch einen FILE-Pointer in eine Tabelle
repräsentiert ist. Ein Pointer auf dein Datei-Objekt kann darin aber
nicht enthalten sein, da es sich bei diesem Objekt ja nur um eine Kopie
handelt, von der die Standardbibliothek nichts weiß.

Generell sollte auf die Inhalte der FILE-Struktur nicht direkt, sondern
nur über die f*-Funktionen der Standardbibliothek zugegriffen werden.

von mh (Gast)


Lesenswert?

Aus https://en.cppreference.com/w/cpp/io/c
1
C streams are objects of type std::FILE that can only be accessed and manipulated through pointers of type std::FILE* (Note: while it may be possible to create a local object of type std::FILE by dereferencing and copying a valid std::FILE*, using the address of such copy in the I/O functions is undefined behavior). Each C stream is associated with an external physical device (file, standard input stream, printer, serial port, etc).

Das ist genau das, was du nicht machen darfst.

von Wilhelm M. (wimalopaan)


Lesenswert?

Dein RAII-Ansatz ist im Prinzip richtig, allerdings kannst Du das eben 
nicht mit Vererbung umsetzen, sondern mit Komposition.

Der FILE* Pointer ist ja nur ein Ressource-Handle, deswegen muss man 
überlegen, was Du für eine Semantik haben möchtest, wenn Du 
Datei-Objekte kopierst. Am besten machst Du die Klasse nur 
vertausch-/verschiedbbar, aber nicht kopierbar (analog zu 
std::ofstream).

von Dirk K. (merciless)


Lesenswert?

In the Dessert I can't remember my Name schrieb:
> Dirk K. schrieb:
>> ein gutes Buch über C++ würde dir auch helfen
>
> Danke für Dein schonungsloses Feedback. Welches Buch kannst Du
> empfehlen?

Hier ist eine Liste:
https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list/388282#388282

Mein Tipp: Den Stroustrup nicht als Anfänger lesen,
das ist harter Tobak. Nimm "C++ Primer" für den Einstieg.
Weiterführend Nicolai Josuttis (erklärt STL) und
Scott Meyers (für best practices).

Ich empfehle dir auch, C und C++ weitestgehend zu
trennen, so lange du dich einarbeitest: fprintf() ist C,
in C++ verwendet man dafür streams.

merciless

von mh (Gast)


Lesenswert?

Dirk K. schrieb:
> Ich empfehle dir auch, C und C++ weitestgehend zu
> trennen, so lange du dich einarbeitest: fprintf() ist C,
> in C++ verwendet man dafür streams.

Ich stimme dir zu. Es ist wichtig C und C++ zu trennen. Allerdings 
fprintf genauso C++ wie es C ist. Und aktuell ist es die einzig 
sinnvolle Möglichkeit für die formatierte Ausgabe von Daten im Standard. 
streams haben keinen Vorteil gegenüber den stdio Funktionen, wenn ein 
guter Compiler mit aktivierten Warnungen benutzt wird. Das ändert sich 
zum Glück mit C++20 dank std::format.

von Wilhelm M. (wimalopaan)


Lesenswert?

mh schrieb:
> Dirk K. schrieb:
>> Ich empfehle dir auch, C und C++ weitestgehend zu
>> trennen, so lange du dich einarbeitest: fprintf() ist C,
>> in C++ verwendet man dafür streams.
>
> Ich stimme dir zu. Es ist wichtig C und C++ zu trennen. Allerdings
> fprintf genauso C++ wie es C ist. Und aktuell ist es die einzig
> sinnvolle Möglichkeit für die formatierte Ausgabe von Daten im Standard.
> streams haben keinen Vorteil gegenüber den stdio Funktionen, wenn ein
> guter Compiler mit aktivierten Warnungen benutzt wird. Das ändert sich
> zum Glück mit C++20 dank std::format.

Oder seit C++17 mit:

https://en.cppreference.com/w/cpp/utility/to_chars

von mh (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Oder seit C++17 mit:

Das ist kein Ersatz für fprintf. Es ist eine auf einen Wert 
eingeschränkte Variante von printf für Iteratoren. Wenn man etwas 
komplexere Ausgaben hat, hat man schnell die gleichen Probleme wie mit 
den streams.

von Wilhelm M. (wimalopaan)


Lesenswert?

mh schrieb:
> Das ist kein Ersatz für fprintf.

Habe ich nicht behauptet.

Allerdings hast Du gesagt, std::fprintf() wäre die einzige sinnvolle 
Möglichkeit sei, eine formatierte Ausgabe zu erzeugen.

Ich finde std:.to_chars() sehr sinnvoll, weil es per definitionem auch 
die schnellste Möglichkeit sein soll in der stdlibc++.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.