2011-12-15 3 views
25

У меня есть два класса, которые определены в отдельных файлах заголовков. Каждый файл имеет поле, которое является типом другого класса. Теперь я включил в заголовок каждого файла заголовок другого файла, но компилятор генерирует ошибки. Что мне не хватает?Файлы заголовков C++, включая друг друга, взаимно

+0

Какое «поле»? – fge

ответ

33

Вы не можете иметь в каждом классе «поле, которое является типом другого класса»; это было бы рекурсивным определением, и не только компилятор не смог бы сделать из этого никакого смысла, он даже не имеет логического смысла.

Каждый класс, имеющий поле, которое является типом другого класса, является видом невозможности, которую вы видите только в M.C. Эшера чертежи, или анимации их, как этот:

 

                                            based on Escher's "Print Gallery" Lithograph, 1956

                                            Источник: escherdroste.math.leidenuniv.nl

                        на основе Эшера «Print Gallery» Литография, 1956, see Wikipedia

 

Одно из двух полей должны быть указателем, чтобы сломать рекурсивное сдерживание, и избегайте логической невозможности.

Это приводит нас к следующей проблеме: если класс B должен содержать экземпляр класса A, то, очевидно, A должен быть объявлен перед классом B, так что A уже известно компилятору при компиляции B. Но если класс A объявлен перед классом B, как мы можем объявить указатель на B в A? Класс B пока неизвестен в то время, когда A скомпилирован! Ответ на это - это специальная конструкция, известная как форвардная декларация, которая существует именно для того, чтобы приспособить такие ситуации. Передняя декларация класса B выглядит следующим образом:

class B; 

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

Таким образом, полное решение проблемы выглядит следующим образом:

файл «Ах»:

/* This is called a "forward declaration". We use it to tell the compiler that the 
    identifier "B" will from now on stand for a class, and this class will be defined 
    later. We will not be able to make any use of "B" before it has been defined, but 
    we will at least be able to declare pointers to it. */ 
class B; 

class A 
{ 
    /* We cannot have a field of type "B" here, because it has not been defined yet. 
     However, with the forward declaration we have told the compiler that "B" is a 
     class, so we can at least have a field which is a pointer to "B". */ 
    B* pb; 
} 

файл «B.h»:

#include "A.h" 

class B 
{ 
    /* the compiler now knows the size of "A", so we can have a field of type "A". */ 
    A a; 
} 
+1

[Источник анимации] (http://escherdroste.math.leidenuniv.nl/index.php?menu=animation) – Potatoswatter

+0

@Potatoswatter благодарит! Я изменил заголовок изображения. –

17

Вы не должны включать файлы заголовков внутри других, просто укажите файлы заголовков в исходные файлы.

В заголовках можно использовать опережающее объявление:

// In Class1.h 
class Class2; 

// In class2.h 
class Class1; 

Также вы можете защитить от файла включаются дважды с использованием препроцессора:

// Class1.h 
#ifndef __CLASS_1_H 
#define __CLASS_1_H 

// content 

#endif 
+6

ПРИМЕЧАНИЕ: форвардные объявления означают, что вы можете использовать этот класс только как указатель в файле заголовка. Не экземпляр класса. –

+0

Хорошо, я забыл упомянуть об этом! –

+1

Вы также можете использовать его как тип возврата или тип параметра в объявлении функций и как часть ссылочного типа. –

2

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

12

Я знаю, что это старая тема, но, может быть, вы все еще заинтересованы в решении!

Фактически на C++ вы можете использовать два класса рекурсивно, не используя указатели, и вот как это сделать.

файл: хиджры

#include <b.h> 

class A { 
    B<> b; 
} 

файл: b.h

class A; 

template<typename T = A> 
class B { 
    T a; 
} 

файла: main.cpp

#include "a.h"  
A a; 

и это все!

, конечно, это только для любопытства :)

+2

Я дам вам +1 для этого :-) – MegaManX

+0

Спасибо, это твой вид :) – Boynux

+0

Вы пытались его скомпилировать? MSVC дает мне ошибку: «B :: a» использует неопределенный класс «A» ' – kinokijuf

Смежные вопросы