Forum: PC-Programmierung C++/Qt 4.8.1: Überflüssige Vorwärtsdeklarationen vermeiden


von Flo (Gast)


Lesenswert?

Hallo C++ler,

ich schreibe gerade ein Programm, das aus einer GUI "MainWindow" und 
einer Datenverarbeitung "Core" besteht.
Ansich funktioniert alles, allerdings muss ich im Header von Core jede 
verwendete Klasse vorwärtsdeklarieren.
Das finde ich äußerst merkwürdig, umständlich und vor allem hässlich :D
Tue ich das nicht, wird die Fehlermeldung "ISO C++ forbids declaration 
of 'SerialTransmitter' with no type" ausgegeben.
Warum auch immer, wird also die Klasse nicht durch den Header 
deklariert.
Könnt ihr mir weiter helfen, was habe ich übersehen?
Das Problem trat erst auf, seit ich mein Programm in GUI und Core 
gesplittet hatte.
Irgendwie habe ich das Gefühl es hängt mit dem Namespace zusammen.

Gruß und Dank
Flo



main.cpp
1
#include <QApplication>
2
3
#include "core.h"
4
#include "mainwindow.h"
5
6
int main(int argc, char *argv[])
7
{
8
    QApplication gui(argc, argv);
9
10
    Core *theCore = new Core();
11
    MainWindow *theMainWindow = new MainWindow(theCore);
12
    theMainWindow->show();
13
14
    return gui.exec();
15
}

Hier muss ich alle verwendeten Klassen vorwärtsdeklarieren, hier zwei 
Beispielklassen:
core.h
1
#ifndef CORE_H
2
#define CORE_H
3
4
#include "serialtransmitter.h"
5
class SerialTransmitter;
6
#include "serialreceiver.h"
7
class SerialReceiver;
8
9
class Core : public QObject
10
{
11
    Q_OBJECT
12
13
public:
14
    explicit Core();
15
16
    SerialTransmitter *theSerialTransmitter;
17
    SerialReceiver *theSerialReceiver;
18
19
public slots:
20
    ...
21
22
public:
23
    ...
24
25
private:
26
    ...
27
};
28
29
#endif // CORE_H

core.cpp
1
#include "core.h"
2
3
Core::Core() : QObject()
4
{
5
    theSerialPort = NULL;
6
7
    theSerialTransmitter = new SerialTransmitter(this);
8
    theSerialReceiver = new SerialReceiver(this);
9
}

mainwindow.h
1
#ifndef MAINWINDOW_H
2
#define MAINWINDOW_H
3
4
#include <QtGui>
5
6
#include "DEF.h"
7
#include "core.h"
8
#include "mappanel.h"
9
#include "viewpanel.h"
10
#include "controlpanel.h"
11
12
namespace Ui
13
{
14
    class MainWindow;
15
}
16
17
class MainWindow : public QMainWindow
18
{
19
    Q_OBJECT
20
21
public:
22
    explicit MainWindow(Core *core);
23
    ~MainWindow();
24
25
private:
26
    Core *theCore;
27
    Ui::MainWindow *ui;
28
29
    ...
30
31
    MapPanel *theMapPanel;
32
    ViewPanel *theViewPanel;
33
    ControlPanel *theControlPanel;
34
};
35
36
#endif // MAINWINDOW_H

mainwindow.cpp
1
#include "mainwindow.h"
2
#include "ui_mainwindow.h"
3
4
MainWindow::MainWindow(Core *core) : QMainWindow(), ui(new Ui::MainWindow)
5
{
6
    ui->setupUi(this);
7
    theCore = core;
8
9
    theMapPanel = new MapPanel(theCore);
10
    theViewPanel = new ViewPanel(theCore);
11
    theControlPanel = new ControlPanel(theCore);
12
13
    ...
14
}
15
16
MainWindow::~MainWindow()
17
{
18
    delete ui;
19
}

von radiostar (Gast)


Lesenswert?

Frage: passiert das auch, wenn main.cpp compiliert wird? Außerdem 
vermisse ich ein "#include <QObject>" in core.h, denn woher soll der 
Compiler wissen, was ein QObject ist?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das erscheint merkwürdig:
1
#include "serialtransmitter.h"
2
class SerialTransmitter;
3
#include "serialreceiver.h"
4
class SerialReceiver;

Was ist denn in SerialTransmitter deklariert?

von Flo (Gast)


Lesenswert?

Hallo radiostar,
Tatsache, QObject war nicht importiert, Danke für den Hinweis.
Dennoch funktioniert es nicht ohne Vorwärtsdefinitionen ...

Hallo Rufus,
hier SerialTransmitter:

serialtransmitter.h
1
#ifndef SERIALPORT_H
2
#define SERIALPORT_H
3
4
#include <QObject>
5
6
#include "core.h"
7
class Core;
8
#include "qextserialport.h"
9
#include "qextserialenumerator.h"
10
#include "serialportdialog.h"
11
class SerialPortDialog;
12
13
class SerialTransmitter : public QObject
14
{
15
    Q_OBJECT
16
17
public:
18
    explicit SerialTransmitter(Core *core);
19
20
private:
21
    Core *theCore;
22
23
    SerialPortDialog *theSerialPortDialog;
24
25
public slots:
26
    ...
27
28
public:
29
    ...
30
};
31
32
#endif // SERIALPORT_H

serialtransmitter.cpp
1
#include "serialtransmitter.h"
2
3
SerialTransmitter::SerialTransmitter(Core *core) : QObject()
4
{
5
    theCore = core;
6
7
    theSerialPortDialog = NULL;
8
}

Ich habe die Klassen nun alle sehr stark vereinfacht, falls ihr Methoden 
o.ä. zum Verständnis benötigt, sagt einfach Bescheid! ;)

Viele Grüße
Flo

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das ist merkwürdig. In serialtransmitter.h ist die Klasse deklariert, 
und nach dem Einbinden davon verwendest Du nochmal eine 
"Vorwärts"deklaration?

Was soll das?

von Flo (Gast)


Lesenswert?

Hallo Rufus,
mir kommt es genauso spanisch vor wie Dir!
Ohne die "Vorwärts"deklaration nach dem Einbinden des Headers compiliert 
er nicht: "ISO C++ forbids declaration of 'myUglyClass' with no type"
Ich muss alle Klassen hinter Core nach"vorwärts"deklarieren, in 
MainWindow funktioniert alles, wie es soll ...

von Malte S. (maltest)


Lesenswert?

Flo schrieb:
> serialtransmitter.h:
> #ifndef SERIALPORT_H
> #define SERIALPORT_H

Der Include-Guard entspricht nicht dem Dateinamen. Gibt es evtl. mehrere 
Dateien, die dank Copy/Paste SERIALPORT_H #definen?

Mit
#pragma once
wäre das nicht passiert.

von chris (Gast)


Lesenswert?

in serialtransmitter.h

#ifndef SERIALPORT_H

Wieso nicht #ifndef SERIALTRANSMITTER_H ?

Nicht dass das SERIALPORT_H schonmal defined wurde.

von Flo (Gast)


Lesenswert?

Hallo Malte und Chris,
ihr habt Relikte aus der alten Namensgebung gefunden ;)
Das war aber leider auch nicht der ausschlaggebende Fehler ... :(
Trotzdem Danke!

von Flo (Gast)


Lesenswert?

Vielleicht gerade noch einmal eine etwas ausführlichere 
Fehlerbeschreibung:
Wenn ich in core.h die "Vorwärts"deklaration "class SerialTransmitter;" 
auskommentiere, erhalte ich genau folgende Ausgaben des Compilers:
    In file included from serialtransmitter.h - serialtransmitter.h
    from serialtransmitter.cpp - serialtransmitter.cpp
(!) ISO C++ forbids declaration of 'SerialTransmitter' with no type - 
core.h
(!) expected ';' before '*' token - core.h

von radiostar (Gast)


Lesenswert?

in SerialTransmitter hast Du eine Variable vom Typ Core und in Core eine 
Variable vom Typ SerialTransmitter. Das kann nicht ohne 
Vorwärtsdeklaration funktionieren, denn eine der beiden Klassen muß ja 
zuerst definiert werden.

von Peter II (Gast)


Lesenswert?

du kannst aber die Vorwärtsdeklarationen in die header datei schreiben 
und zwar vor dem include guard.

von Malte S. (maltest)


Lesenswert?

radiostar schrieb:
> in SerialTransmitter hast Du eine Variable vom Typ Core und in Core eine
> Variable vom Typ SerialTransmitter. Das kann nicht ohne
> Vorwärtsdeklaration funktionieren, denn eine der beiden Klassen muß ja
> zuerst definiert werden.

Ja, aber es reicht, eine der Klassen vorwärts zu deklarieren. Und eine 
"Vorwärts"deklaration nach einbinden des Headers ist merkwürdig...

Aber klar:

Das #include "core.h" muss aus serialtransmitter.h raus oder das 
#include "serialtransmitter.h" aus core.h und die jeweils andere Klasse 
muss dann vorwärsdeklariert werden.
Den dazugehörigen Header dann nur in der .cpp-Datei einbinden.

von radiostar (Gast)


Lesenswert?

Malte S. schrieb:
> Ja, aber es reicht, eine der Klassen vorwärts zu deklarieren. Und eine
> "Vorwärts"deklaration nach einbinden des Headers ist merkwürdig...

Das liegt an der Art, wie die header inkludiert werden:

core.h bindet serialtransmitter.h ein, das bindet core.h ein. Das aber 
funktioniert nicht, weil CORE_H schon definiert ist. Also bleibt die 
Klasse Core undefiniert in serialtransmitter.h. Eigentlich ganz logisch.

von Malte S. (maltest)


Lesenswert?

radiostar schrieb:
> Malte S. schrieb:
>> Ja, aber es reicht, eine der Klassen vorwärts zu deklarieren. Und eine
>> "Vorwärts"deklaration nach einbinden des Headers ist merkwürdig...
>
> Das liegt an der Art, wie die header inkludiert werden:
>
> core.h bindet serialtransmitter.h ein, das bindet core.h ein. Das aber
> funktioniert nicht, weil CORE_H schon definiert ist. Also bleibt die
> Klasse Core undefiniert in serialtransmitter.h. Eigentlich ganz logisch.

Sag ich doch :)
Gegenseitiges #include ist schlecht fürs Karma.

a.h:
1
#pragma once
2
3
class B;
4
5
class A {
6
    A();
7
    B* myB;
8
};
b.h:
1
#pramga once
2
3
#include "a.h"
4
5
class B {
6
    B();
7
    A* myA;
8
};

a.cpp:
1
#include "a.h"
2
#include "b.h"
3
4
A::A()
5
    : myB(new B()) {
6
}

b.cpp:
1
#include "b.h"
2
3
B::B()
4
    : myA(new A()) {
5
}

Abgesehen vom entstehenden Speicherleck und minus Tipp- und sonstigen 
Fehlern sollte das zeigen, wie du die Abhängigkeiten auflösen kannst.

* In a.h ist kein #include "b.h"
* Also darf die Klasse B im Header a.h auch nur als Zeiger oder Referenz 
auftauchen.

von Peter II (Gast)


Lesenswert?

Malte S. schrieb:

> a.h:#pragma once
>
> class B;
>
> class A {
>     A();
>     B* myB;
> };

ich verwenden immer:
1
class A;
2
3
#ifndef DATEINAME
4
#define DATEINAME
5
6
#include b
7
 
8
class A {
9
    A();
10
    B* myB;
11
};
12
13
#endif

damit ist man das Problem auch los. Ich finde es sehr unschön in fremden 
Dateien eine vorwärtsdeklaration für fremnde dinge zu haben.

von Malte S. (maltest)


Lesenswert?

Auch eine Variante.
Da halte ich wiederum dagegen, dass die Vorwärtsdeklaration sonst ja das 
#include ersetzt und dadurch auch dokumentiert, dass eben nur 
vorwärtsdeklariert wurde mit allen Einschränkungen, die das bringt. Bei 
einem gegenseitigen #include mit magischen Vorwärtsdeklarationen wird 
das versteckt.
Geschmackssache.

von Flo (Gast)


Lesenswert?

Boa Jungs, ihr seid Klasse!!!
Das klingt verdammt logisch ... und es funktioniert!
Vielen Dank für eure Mühe! :)

von radiostar (Gast)


Lesenswert?

Bitte sehr. War uns eine Ehre.

von Rolf M. (rmagnus)


Lesenswert?

Ich mache eigentlich immer nur dann ein #include, wenn es dort auch 
wirklich nötig ist. Wenn für ein File eine Vorwärtsdeklaration reicht, 
mache ich nur die. Macht Qt selbst übrigens intern auch so. Das hält den 
Rattenschwanz an inkludierten Headern kürzer, und die Probleme mit 
zyklischen Abhängigkeiten ergeben sich gar nicht erst.

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.