2009-12-11 5 views
9

Код следующего кода компилируется в VC++ 6. Я не понимаю, почему я получаю ошибку компиляции C2079: 'b' uses undefined class 'B' для следующего кода.Перекрестное декларирование класса, похоже, не работает в C++

Класс B Источник

#include "B.h" 

void B::SomeFunction() 
{ 
} 

Класс B Заголовок

#include "A.h" 

struct A; 

class B 
{ 
    public: 
     A a; 
     void SomeFunction(); 
}; 

STRUCT A Header

#include "B.h" 

class B; 

struct A 
{ 
    B b; 
}; 

Я f Я изменил заголовок класса B на следующее, тогда ошибки не будет. Но объявление заголовка не будет наверху!

Класс B Заголовок с декларацией странным заголовком

struct A; 

class B 
{ 
    public: 
     A a; 
     void SomeFunction(); 
}; 

#include "A.h" 
+0

Можете ли вы поместить их в один заголовочный файл? Я знаю, что это не особенно технический ответ, но вы могли бы избежать всего этого, выполнив это, если это осуществимо. – ihtkwot

+0

Неважно, если вы поместите оба определения классов в один и тот же заголовочный файл, все равно нужно прийти первым и не знать размер другого класса. – Peter

+0

спасибо за разъяснение, я все еще очень новичок в этом и надеялся, что я не предлагаю плохой совет, который я был – ihtkwot

ответ

14

Чтобы определить класс или структуру, компилятор должен знать, насколько велика каждая переменная-член класса. Это непротиворечивое заявление. Я только видел, что он используется для указателей и (реже) ссылок.

Помимо этого, то, что вы пытаетесь сделать здесь, не может быть выполнено. Вы не можете иметь класс A, содержащий объект другого класса B, который содержит объект класса A. Вы можете, однако, класс А содержит указатель для класса B, который содержит объект класса A.

B.каст

#include "B.h" 

void B::SomeFunction() 
{ 
} 

B.h

#ifndef __B_h__ // idempotence - keep header from being included multiple times 
#define __B_h__ 
#include "A.h" 

class B 
{ 
public: 
    A a; 
    void SomeFunction(); 
}; 

#endif // __B_h__ 

хиджры

#ifndef __A_h__ // idempotence - keep header from being included multiple times 
#define __A_h__ 
#include "B.h" 

class B; // forward declaration 

struct A 
{ 
    B *b; // use a pointer here, not an object 
}; 

#endif // __A_h__ 

Две точки. Во-первых, не забудьте использовать некоторую форму идемпотенции, чтобы держать заголовки включенными несколько раз в единицу компиляции. Во-вторых, поймите, что в C++ единственное различие между классами и структурами - это уровень видимости по умолчанию - классы по умолчанию используют конфиденциальную видимость, а по умолчанию структура использует общедоступную видимость. Следующие определения функционально эквивалентны в C++.

class MyClass 
{ 
public: // classes use private visibility by default 
    int i; 
    MyClass() : i(13) { } 
}; 

struct MyStruct 
{ 
    int i; 
    MyStruct() : i(13) { } 
}; 
+5

Двойные подчеркивания зарезервированы для компилятора. Используйте другое соглашение об именах. – GManNickG

+0

@GMan: Я всегда использовал это соглашение для маркировки idempotence, когда компилятор не создает их автоматически. Является ли это специфическим для компилятора или это в спецификации C++? –

+1

@Matt, он является частью спецификации C++, что такие идентификаторы зарезервированы. Я лично использую «HEADER_PATH_TO_HEADER_H_INCLUDED» или «HEADER_PATH_TO_HEADER_INCLUDED» (если нет «.h»). –

3
public: 
    A a; 

Вы пытаетесь создать объект А с только вперед декларации. Компилятор в данный момент (только с прямым сообщением) не может определить размер объекта A и, следовательно, он не может выделить память, необходимую для A. Таким образом, вы не можете создавать объекты только с прямым объявлением.

Вместо заменить:

A* a; 

указатель или ссылка на без определения класса А будет работать нормально.

3

Две проблемы выпрыгивают на меня здесь.

1: Вы написали Struct A вместо struct A; обратите внимание на нижний регистр «s». Ваш компилятор может рассмотреть эквивалент, но я не думаю, что это стандартный C++.

Вы определили круглую ссылку между A и B. Каждый объект A должен содержать объект B, но каждый объект B должен содержать объект A! Это противоречие и никогда не будет работать так, как вы этого хотите. Обычный метод C++ для решения этой проблемы заключается в использовании указателей или ссылок для A::b или B::a (или обоих).

0

Вы также можете указать A.h из B.h и B.h из A.h. Вы должны, по крайней мере, использовать макросы препроцессора:

#ifndef __A_H__ 
#define __A_H__ 

// A.h contents 

#endif 

, так что файл не будет включен более одного раза.

+0

В фактическом коде макросы препроцессора определены, но здесь не показано, чтобы сделать код более простым. – Lopper

+0

Двойные подчеркивания зарезервированы для компилятора. Используйте другое соглашение об именах. – GManNickG

0

Если вы создадите экземпляр A, который создаст экземпляр B (member var), который создаст экземпляр A (member var), который создаст экземпляр B, который создаст экземпляр A и так далее on ... Компилятор не должен допускать этого, так как он требует бесконечной памяти.

Чтобы решить эту проблему, A или B должны использовать ссылку/указатель на другой класс.

2

Вперед декларации, как

struct A; 

или

class A; 

ввести в качестве неполного типа и остается неполным, пока конец определения типа никогда достигнуто. Есть вещи, которые вы можете делать с неполными типами и вещами, которые вы не можете сделать. Вы можете

  1. Объявите переменные (или члены) типа «указатель на» и «ссылка на»
  2. Declare функции, которые принимают аргументы типа А или возвращающих типа А

Вы можете «т

  1. Объявите переменные (ни члены) типа А
  2. Разыменовывание указателей на или получить доступ к любым членам ссылки на
  3. Определение подклассов А.

В своем коде вы пытаетесь объявить член структуры неполного типа. Это незаконно. Разрешены только указатели и ссылки.