Проблема с прямым объявлением C ++ при вызове метода
-
19-08-2019 - |
Вопрос
У меня есть проблема, которая, как я думаю, связана с предварительными объявлениями, но, возможно, нет.
Вот соответствующий код:
A.h
#ifndef A_H_
#define A_H_
#include "B.h"
class A
{
private:
B b;
public:
A() : b(*this) {}
void bar() {}
};
#endif /*A_H_*/
Б.х
#ifndef B_H_
#define B_H_
#include "A.h"
class A;
class B
{
private:
A& a;
public:
B(A& a) : a(a) {}
void foo() { /*a.bar();*/ } //doesn't compile
};
#endif /*B_H_*/
main.cpp
#include "A.h"
int main()
{
A a;
return 0;
}
Проблема, по-видимому, связана с вызовом A::bar() .Программа успешно компилируется до тех пор, пока я не попытаюсь вызвать этот метод, после чего я получаю две ошибки:
ошибка:недопустимое использование неполного типа ‘struct A’
ошибка:прямое объявление ‘структуры A’
Я предполагаю, что это связано с тем, что A::bar() еще предстоит определить или объявить, поскольку оба заголовка ссылаются друг на друга.Тем не менее, я пересылаю объявленный класс A и нахожусь в недоумении относительно того, что еще мне нужно сделать.Я новичок в C ++, поэтому, пожалуйста, простите меня.Я не смог найти ответ на этот вопрос больше нигде в Интернете.Как всегда, заранее спасибо!
Решение
У вас есть циклическая ссылка, поэтому вам нужно разделить B.h.Попробуйте что-то вроде:
Б.х:
#ifndef B_H_
#define B_H_
// don't include A.h here!
class A;
class B
{
private:
A& a;
public:
B(A& a) : a(a) {}
void foo();
};
#endif /*B_H_*/
B.cpp:
#include "B.h"
#include "A.h"
void B::foo() { a.bar(); } // now you're ok
Редактировать:объяснение, почему вам нужно разделить его на два файла:
Класс B
содержит ссылку на A
, который может быть так называемым неполный Тип.Вы не можете вызывать какие-либо функции на нем, потому что компилятор еще не знает, какого черта A
это ... это просто знает, что это какой-то класс.Как только вы включите A.h (в cpp-файл), затем A
это законченный тип, и вы можете делать с ним все, что вам заблагорассудится.
Вы не можете сохранить все это в одном заголовочном файле, потому что вы получите циклическую ссылку.Вы предотвращаете бесконечный цикл с помощью своих защитных устройств включения, но вы получаете то, чего не хотите.Посмотрите, что выдает компилятор при компиляции main.cpp как у вас было раньше:
// #include "A.h" ==>
#define A_H_
// #include "B.h" ==>
#define B_H_
// #include "A.h" ==> nothing happens! (since A_H_ is already defined)
class A;
class B {
private:
A& a;
public:
B(A& a) : a(a) {}
void foo() { a.bar(); } // <-- what the heck is A here?
// it's not defined until below
};
class A {
private:
B b;
public:
A() : b(*this) {}
void bar() {}
};
int main() {
A a;
return 0;
}
Другие советы
Линия #include<file.h>
просто заменит строку содержимым file.h
.Поэтому, когда компьютер пытается скомпилировать ваш main.cpp
, это соберет все воедино, что выглядит следующим образом.В том месте, где вы хотите использовать A::bar(), оно не было определено.
// result from #include "A.h"
#ifndef A_H_
#define A_H_
// Now, #include "B.h" in A.h will get you the following
#ifndef B_H_
#define B_H_
// here you include "A.h" again, but now it has no effect
// since A_H_ is already defined
class A;
class B
{
private:
A& a;
public:
B(A& a) : a(a) {}
// Oops, you want to use a.bar() but it is not defined yet
void foo() { /*a.bar();*/ }
};
#endif /*B_H_*/
class A
{
private:
B b;
public:
A() : b(*this) {}
void bar() {}
};
#endif /*A_H_*/
// now this is your main function
int main()
{
A a;
return 0;
}
Как уже упоминали некоторые другие, циклическая ссылка, по-видимому, является вашей проблемой.Другой фразой для этого было бы "взаимная зависимость".Однако вместо того, чтобы пытаться найти правильный синтаксис для компиляции и запуска вашего приложения (я предполагаю, что реальная проблема существует в программе, немного более продвинутой, чем та, которую вы опубликовали), я бы посоветовал вам подойти к проблеме с точки зрения объектно-ориентированного проектирования.
Как общее правило, по возможности следует избегать взаимной зависимости.Я уже сталкивался с этой проблемой в своем собственном коде раньше (что привело к нескольким дням отладки, я рвал на себе волосы и проклинал свой компилятор), и вот как я, наконец, смог ее преодолеть.Я представлю смягченную версию моей собственной проблемы в качестве конкретного примера того, как решить проблему, так что, надеюсь, вы сможете извлечь скрытый смысл, стоящий за всем этим, и в конце концов все это обретет смысл.
Допустим, у нас есть два класса:Данные и анализатор данных
Данные содержат ссылку на DataAnalyzer (используется для анализа данных), а DataAnalyzer содержит ссылку на Data (данные, подлежащие анализу) - взаимная зависимость!Чтобы устранить эту зависимость, мы извлекаем интерфейс (на C ++, чисто виртуальный класс) из DataAnalyzer, который определяет общедоступные методы / атрибуты, требуемые для DataAnalyzer.Это может выглядеть примерно так:
class IAnalyzer
{
public:
virtual void Analyze () = 0;
};
Когда мы определяем DataAnalyzer, мы делаем это следующим образом:
class DataAnalyzer : public IAnalyzer
{
public:
DataAnalyzer (Data* data);
virtual void Analyze (); // defined in DataAnalyzer.cpp
};
И данные выглядят следующим образом:
class Data
{
public:
Data ();
IAnalyzer* Analyzer;
};
Где-то в вашем классе контроллера у вас может быть что-то вроде:
void main ()
{
Data* data = new Data ();
data->Analyzer = new DataAnalyzer (data);
}
Теперь данные существуют сами по себе (насколько известно, IAnalyzer не требует ссылки на данные), и только DataAnalyzer зависит от данных.Если вы хотите продолжить, вы могли бы продолжить удалять зависимость DataAnalyzer от Data, но для простого разрыва взаимной зависимости этого должно быть достаточно.
Предупреждение: Я не тестировал этот код на компиляцию, поэтому для правильной компиляции и запуска может потребоваться небольшая корректировка.
Удачи!
Если вы действительно хотите, чтобы B::foo был встроенным, вы могли бы выполнить реализацию в B.h, хотя я бы не рекомендовал этого.
Б.х:
#ifndef B_H_
#define B_H_
// Forward declaration of A.
class A;
class B
{
private:
A& a;
public:
B(A& a) : a(a) {}
void foo();
};
// Include definition of A after definition of B.
#include "A.h"
inline void B::foo()
{
a.bar();
}
#endif /*B_H_*/
A.h:
// Include definition of B before definition of A.
// This must be done before the ifndef A_H_ include sentry,
// otherwise A.h cannot be included without including A.h first.
#include "B.h"
#ifndef A_H_
#define A_H_
class A
{
private:
B b;
public:
A() : b(*this) {}
void bar() {}
};
#endif /*A_H_*/
В B.h вы включаете A.h, а также передаете объявление A.
Вам нужно разделить B.h на B.h и B.cpp , или удалить прямое объявление.
PS У вас также есть циклическая зависимость.A.h включает B.h, и наоборот.Однако ваши охранники улавливают проблему ;)
Чтобы добавить к другому ответу (циклическая ссылка, которая является правильным ответом), если вы переходите с C # / Java, поймите, что C ++ отличается тем, что файлы анализируются по порядку (вместо того, чтобы рассматриваться как единое целое).Поэтому вам нужно быть осторожным, чтобы убедиться, что все определено до того, как оно будет использовано, в фактическом порядке включения файлов (и / или отдельной функциональности в файлах .cpp по мере необходимости).