Mam co następuje:
Ubuntu Linux 20.04.2
GCC w wersji 9.3.0
Qt w wersji 5.12.8
Qt Creator w wersji 4.11.0
Chce zrobić w C++ dokładnie to samo, co w Java i w C# można zrobić bez żadnego problemu.
Chciałbym mieć klasę A, w której jest referencja do obiektu klasy B oraz klasę B, w której jest referencja do obiektu klasy A.
W efekcie mam taką klasę A (plik *.h i *.cpp):
#ifndef TESTCLASSA_H
#define TESTCLASSA_H
#include "testclassb.h"
class TestClassA
{
public:
TestClassA();
TestClassB * RefB;
int I;
void foo();
void bar();
};
#endif // TESTCLASSA_H
#ifndef TESTCLASSB_H
#define TESTCLASSB_H
#include "testclassa.h"
class TestClassB
{
public:
TestClassB();
TestClassA * RefA;
int I;
void foo();
void bar();
};
#endif // TESTCLASSB_H
Klasa B jest analogiczna do klasy A:
#include "testclassa.h"
TestClassA::TestClassA()
{
I = 0;
RefB = nullptr;
}
void TestClassA::foo()
{
I++;
}
void TestClassA::bar()
{
RefB->foo();
}
#include "testclassb.h"
TestClassB::TestClassB()
{
I = 0;
RefA = nullptr;
}
void TestClassB::foo()
{
I++;
}
void TestClassB::bar()
{
RefA->foo();
}
W całej aplikacji jest też MainWindow zdefiniowany nastepująco:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
#include "testclassa.h"
#include "testclassb.h"
using namespace std::chrono;
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
TestClassA * TestClassA_;
TestClassB * TestClassB_;
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
TestClassA_ = new TestClassA();
TestClassB_ = new TestClassB();
TestClassA_->RefB = TestClassB_;
TestClassB_->RefA = TestClassA_;
}
MainWindow::~MainWindow()
{
delete TestClassA_;
delete TestClassB_;
delete ui;
}
To, co powyżej to możliwie najprostszy sposób zobrazowania problemu, nie chodzi o rozważanie słuszności takiej architektury docelowego projektu. w C# nie ma z tym problemu. Natomiast tutaj, jak próbuję to skompilować, to przy linii TestClass * RefA
w klasie TestClassA
pokazuje się ‘TestClassA’ does not name a type; did you mean ‘TestClassB’
. W drugiej klasie dokładnie to samo. W samym IDE, jak podążam za typem (klawisz F2, przejście do deklaracji pola lub typu), to IDE widzi definicje klas nawzajem, czyli jest prawidłowo.
Skompilowanie tego jako C++20 nie rozwiązuje problemu. Dopiero, jak w jednej z klas usunę linię #include "testclassb.h"
lub #include "testclassa.h"
z pliku *.h i oczywiście wszystko, co pochodzi z tego pliku, to program się kompiluje, jednak, co oczywiste nie ma funkcjonalności, którą ma mieć.
W jaki sposób rozwiązać ten problem zachowując architekturę dwóch klas z referencją wzajemną? Czy to tylko kwestia dodatkowej deklaracji, konfiguracji IDE, konfiguracji kompilatora, czy coś jeszcze innego?
Co do samych referencji, to oczywiście nie muszą być cykliczne, można może na przykład tak, ale problem ciągle pozostaje:
TestClassA TestClassA1 = new TestClassA();
TestClassB TestClassB1 = new TestClassB();
TestClassA TestClassA2 = new TestClassA();
TestClassB TestClassB2 = new TestClassB();
TestClassA TestClassA3 = new TestClassA();
TestClassB TestClassB3 = new TestClassB();
TestClassA1->RefB = TestClassB1;
TestClassB1->RefA = TestClassA2;
TestClassA2->RefB = TestClassB2;
TestClassB2->RefA = TestClassA3;
TestClassA3->RefB = TestClassB3;