C++/Qt 4.8.1: Überflüssige Vorwärtsdeklarationen vermeiden

Gast #3155385
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
}
Gast #3155440
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
Gast #3155453
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 ...
Gast #3155493
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
Gast #3155503
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.
#3155515
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.
Gast #3155522
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.
#3155542
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.
Gast #3155552
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.
#3155561
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.
#3156306
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren