Я знаю проблему рекурсивного определения типов в C ++ для переменных-членов:
#include "B.h"class A
{
B b;
};
#include "A.h"class B
{
A b;
};
Компилятор жалуется на это, потому что невозможно выделить память таким рекурсивным способом.
Чего я не понимаю, так это того, что это применимо и к определениям функций:
#include "B.h"class A
{
B foo();
};
#include "A.h"class B
{
A bar();
};
Компиляторы выдают ту же ошибку:
error: ‘A’ does not name a type
error: ‘B’ does not name a type
Это почему? Мне не имеет смысла, что компилятор должен зарезервировать место для возвращаемого типа. Должен ли я решить эту проблему с помощью указателей и предварительной декларации? По моему опыту (исходя из Java), довольно часто программисты делают эти рекурсивные определения для разработки программного обеспечения. Кажется, это так сложно реализовать в C ++.
Что касается определения функций, все, что вам нужно, это соответствующее предварительное объявление:
class A;
class B;
class A
{
B foo();
};
class B
{
A bar();
};
Это будет компилироваться без проблем. Не стесняйтесь разделить это на два отдельных заголовочных файла с соответствующим предварительным объявлением вместо #include
,
Обратите внимание, что причина, по которой вы не можете объявлять членов класса таким же образом, заключается в том, что это будет эффективно содержать класс в себе. К сожалению, если бы это было возможно, конечным результатом была бы массивная черная дыра, которая поглотит всю жизнь, какой мы ее знаем, и мы, конечно, не хотим, чтобы это произошло …
В любом случае, другой момент, о котором вам нужно помнить, это то, что вы находитесь под влиянием вашего фона Java. Классы работают в C ++ принципиально иначе, чем в Java, несмотря на обманчиво похожий синтаксис. Вам лучше забыть все, что вы знаете о том, как работают классы в Java. Иначе ты будешь сбиваться с пути, вот так. В Java подобный набор объявлений не приводит к тому, что класс эффективно содержит себя, но в C ++ это делает. Это потому, что в Java каждый экземпляр класса действительно является указателем на класс, но в C ++ это сам класс, во плоти и крови.
В C ++ реальным эквивалентом этого вида рекурсивного объявления класса будет:
class A
{
std::shared_ptr<B> b;
};
class B
{
std::shared_ptr<B> A;
};
(игнорируя на данный момент необходимые предварительные декларации, которые применимы и здесь).
И это будет работать в C ++ так же хорошо, как и в Java ( std::shared_ptr<>
part является эквивалентом подсчета ссылок на объекты Java, в некотором роде). это это то, что эквивалентно вашей аналогичной конструкции в Java.
Тебе необходимо предварительная декларация:
class B; // forward declaration
class A
{
B foo();
};
class B
{
A bar();
};
Объявление выше говорит компилятору, что class B
существует, но не то, на что это похоже. Этого достаточно, чтобы использовать его в качестве параметра или возвращаемого типа функции или метода. Затем вы можете уточнить фактическое определение.
Что я не понимаю, так это то, что это похоже на функцию
определения также:
Функции могут быть встроены компилятором, и единственный способ сделать это, когда вы знаете точную структуру возвращаемого типа. И кроме того, компилятор должен знать, как создать тип возвращаемого значения функции
Примечание. Компилятору не нужно знать полное определение возвращаемого типа объявления функции, однако он должен знать об этом при определении.
//file A.h
class B; //forward declaration
class A
{
B foo(); //declaration
};
//file A.cpp
#include "B.h"
B A::foo(){ .... } //definition
//file B.h
class A; //forward declaration
class B
{
A bar(); //declaration, works
A moo() { .... } //Declaration + Definition, Fails to see full definition of `A`
};
//file B.cpp
#include "A.h"
A B::boo() { ... }