Вопрос

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

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

#include "B.h"

void B::SomeFunction()
{
}

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

#include "A.h"

struct A;

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

структурировать Заголовок

#include "B.h"

class B;

struct A
{
    B b;
};

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

Заголовок класса B со странным объявлением заголовка

struct A;

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

#include "A.h"
Это было полезно?

Решение

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

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

B.cpp

#include "B.h"

void B::SomeFunction()
{
}

Б.х

#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__

A.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) { }
};

Другие советы

Прямые объявления, такие как

struct A;

или

class A;

Представьте A как неполный тип и это остается неполным до тех пор, пока не будет достигнуто конечное определение типа.Есть вещи, которые вы можете делать с неполными типами, и вещи, которые вы не можете.Ты можешь

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

Ты не можешь

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

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

public:
    A a;

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

Вместо этого замените на:

A* a;

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

Здесь у меня возникают два вопроса.

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

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

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

#ifndef __A_H__
#define __A_H__

// A.h contents

#endif

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top