Fehler durch zirkulären Verweis

Du bist neu in der Welt von C++? Dann schau hier herein!
Antworten
cyrano1960
Beiträge: 10
Registriert: 31. Dezember 2017 09:41

Fehler durch zirkulären Verweis

Beitrag von cyrano1960 »

Hallo, ich habe jetzt sehr lange nur noch in Java programmiert und mir durch dessen Rücksicht wohl einen unsauberen Stil angewöhnt, bzw. ganz vergessen, wie es im Hintergrund aussieht :( Da bei C++ aber niemand mehr mit dem Besen hinter mir aufräumt kommen auch sofort die ersten Problemchen und ich würde gern Euren Rat einholen.

Ich arbeite mit QT 5.10.0 auf einem Windows 10 PC und nutzen den MinGW und den MSVC-Compiler von Visual Studio 2017. Die Oberfläche erstelle ich in QT-Creator. Das "Problem" äußert sich wie folgt:

Ich habe eine Starterklasse mit einem Hauptfenster, das nur 4 Buttons enthält und die jeweiligen Teilanwendungen startet. Der prinzipielle Aufbau sieht so aus. Ich habe es so reduziert, dass nur noch die relevanten Teile in den Klassen stehen:

starter.h:

Code: Alles auswählen

#ifndef STARTER_H
#define STARTER_H

#include <QMainWindow>
#include <QDebug>
#include <QRegExp>
#include <QChar>
#include <QPlainTextEdit>
#include "config.h"

namespace Ui {
class Starter;
}

class Starter : public QMainWindow
{
    Q_OBJECT

public:
    explicit Starter(QWidget *parent = 0);
    ~Starter();

public slots:

private slots:
    void onBtnConfigClicked();
    void onBtnEndClicked();

private:
    Ui::Starter *ui;
    Config *configDialog;
};

#endif //STARTER_H


starter.cpp

Code: Alles auswählen

#include "starter.h"
#include "ui_starter.h"


Starter::Starter(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::Starter)
{
    ui->setupUi(this);
    connect(ui->btnConfig, SIGNAL(clicked()), this, SLOT(onBtnConfigClicked()));
    connect(ui->btnEnd, SIGNAL(clicked(bool)), this, SLOT(onBtnEndClicked()));
    configDialog = new Config(this);
}

void Starter::onBtnConfigClicked()
{
    configDialog->show();
}


void Starter::onBtnEndClicked()
{
    qApp->quit();
}


Starter::~Starter()
{
    delete configDialog;
    delete ui;
}
config.h

Code: Alles auswählen

#ifndef CONFIG_H
#define CONFIG_H

#include <QDialog>
#include <QString>
#include "starter.h"

namespace Ui {
class Config;
}

class Config : public QDialog
{
    Q_OBJECT

public:
    explicit Config(QWidget *parent = 0);
    ~Config();

private slots:
    void on_btnEnd_clicked();

private:
    Ui::Config *ui;
    //Starter *starter;
};

#endif // CONFIG_H

config.c

Code: Alles auswählen

#include "config.h"
#include "ui_config.h"

Config::Config(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Config)
{
    ui->setupUi(this); 
    connect(ui->btnEnd, SIGNAL(pressed()), this, SLOT(on_btnEnd_clicked()));
}

void Config::on_btnEnd_clicked()
{
    this->close();
}


Config::~Config()
{
    delete ui;
}

Aus der Config-Klasse möchte ich dann auf Elemente der Starter-Klasse zugreifen und deshalb habe ich den Include auf starter.h hinzugefügt und eine Zeigervariable vom Typ Starter erzeugt. Diese wollte ich dann dem parent-Instanz zuweisen (aber so weit komme ich gar nicht). Beim Compilieren erhielt ich mit MinGW die Fehlermeldung:

C:\Users\werner\Documents\QT-Projekte\TestReader\config.h:26: Fehler: 'Starter' does not name a type
Starter *starter;
^


Der Editor erkennt die Klasse allerdings, wenn ich die Variable so erzeugt habe, zeigt mit die Autovervollständigung z. B. Methoden der Klasse an.
Daraufhin habe ich die Zeile auskommentiert, erhalte aber immer noch einen Fehler:

C:\Users\werner\Documents\QT-Projekte\TestReader\starter.h:31: Fehler: 'Config' does not name a type
Config *configDialog;
^


Ich vermute, dass es mit dem zirkulären Verweis zu tun hat, denn ich binde in starter.h ja config.h ein und in config.h starter.h. Allerdings müsste die Define-Direktive doch ein doppeltes Einbinden verhindern, oder? Ich habe es auch schon mit #pragma once probiert, aber das Ergebnis bleibt gleich. Beim Compilieren mit MSVC erhalte ich noch weitere Syntaxfehler. Aus Platzgründen lasse ich die hier erstmal weg. Vielleicht seht Ihr ja ohnehin schon den Fehler.

Und eine Bitte: ich wollte den Aufbau eh ganz anders gestalten, um auf diese gegenseitigen Verweise verzichten zu können. Es geht mir aber um das grundlegende Verständnis, wodurch diese Fehlermeldung erzeugt wird.

Ohne Fehler funktioniert das Ganze (in beiden Compilern), wenn ich in den Include (starter.h) in config.cpp einfüge, dort außerhalb der Klassendeklaration die Zeigervariable erzeuge:

Code: Alles auswählen

#include "config.h"
#include "ui_config.h"
#include "starter.h"

Starter *starter;

Config::Config(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Config)
{
    ui->setupUi(this);
    starter = (Starter*) parent;
    connect(ui->btnEnd, SIGNAL(pressed()), this, SLOT(on_btnEnd_clicked()));
}

void Config::on_btnEnd_clicked()
{
    this->close();
}


Config::~Config()
{
    delete ui;
}
Gefühlt erscheint mir das aber absolut unsauber, denn in diesem Fall wäre *starter ja global und ich müsste in allen weiteren Klassen, die ich noch brauche eine andere Variable erzeugen.

Ich würde mich über Eure Anregungen sehr freuen und wünsche Euch allen schon einmal einen guten Rutsch in ein möglichst bugfreies Jahr 2018!
cyrano1960
Beiträge: 10
Registriert: 31. Dezember 2017 09:41

Re: Fehler durch zirkulären Verweis

Beitrag von cyrano1960 »

Nachtrag:

Mir ist gerade noch etwas eingefallen, wo der Fehler liegen könnte. Ich weiß nicht 100-%-ig genau, wie sich die define-Direktive auswirkt, aber könnte es so sein:

1. Der Compiler beginnt starter.cpp zu übersetzen und trifft dort auf die Zeile:

Code: Alles auswählen

#include "starter.h"
2. Er springt in diese Datei und definiert STARTER_H

3. Zu Beginn von starter.h springt er in config.h und definiert CONFIG_H

4. Der Code von config.h wird in den Quelltext eingefügt.

5. Am Ende springt er zurück in starter.h, fügt aber den dort stehenden Code nicht mehr ein, weil STARTER_H schon definiert ist?

Das wäre dann aber nicht ganz so wie es sein sollte, würde aber den Fehler erklären, dass er die Starter-Klasse gar nicht kennt.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: Fehler durch zirkulären Verweis

Beitrag von Christian81 »

starter.h inkludiert config.h und config.h inkludiert starter.h - das ist das Problem. Die Lösung lautet 'Vorwärtsdaklarationen'. Da Du in der starter.h nur einen Pointer auf Config hast, reicht es dem Compiler dies mitzuteilen - mehr benötigt er dort nicht:

Code: Alles auswählen

class Config;
Das Gleiche gilt übrigens auch für die config.h.

Abgesehen davon ist es trotzdem unklug, wenn sich die Klassen gegenseitig kennen müssen - eine Richtung (in diesem Fall Starter -> Config würde ich sagen) ist immer die bessere Idee.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
cyrano1960
Beiträge: 10
Registriert: 31. Dezember 2017 09:41

Re: Fehler durch zirkulären Verweis

Beitrag von cyrano1960 »

Hallo Christian,

vielen Dank für die schnelle Hilfe. Mir ging es ja auch nur darum, es einfach zu verstehen, warum der Fehler auftritt und das ist mir jetzt klar. Wie schon erwähnt, werde ich aber die gegenseitigen Abhängigkeiten auflösen.

Beste Grüße
Antworten