Взаимные экземпляры классов в C ++ - PullRequest
4 голосов
/ 30 марта 2010

В чем проблема с этим кодом? Здесь у нас есть два файла: classA.h и classB.h

classA.h:

#ifndef _class_a_h_
#define _class_a_h_

#include "classB.h"

class B; //????

class A
{
public:
    A() {
        ptr_b = new B(); //????
    }

    virtual ~A() {
        if(ptr_b) delete ptr_b; //????
                    num_a = 0;
    }

    int num_a;
    B* ptr_b; //????
};

#endif //_class_a_h_

classB.h:

#ifndef _class_b_h_
#define _class_b_h_

#include "classA.h"

class A; //????

class B
{
public:     
    B() { 
        ptr_a = new A(); //????
                    num_b = 0;
    }

    virtual ~B() { 
        if(ptr_a) delete ptr_a; //????
    }

    int num_b;
    A* ptr_a; //????
};

#endif //_class_b_h_

когда я пытаюсь его скомпилировать, компилятор (g ++) говорит:

classB.h: в конструкторе ‘B :: B ()’:

classB.h: 12: ошибка: недопустимое использование неполного типа ‘struct A’

classB.h: 6: ошибка: предварительное объявление "struct A"

classB.h: в деструкторе ‘virtual B :: ~ B ()’:

classB.h: 16: предупреждение: возможна проблема, обнаруженная при вызове оператора удаления:

classB.h: 16: предупреждение: недопустимое использование неполного типа ‘struct A’

classB.h: 6: предупреждение: предварительное объявление ‘struct A’

classB.h: 16: примечание: ни деструктор, ни оператор удаления класса не будут

вызывается, даже если они объявлены, когда класс определен.

Ответы [ 4 ]

9 голосов
/ 30 марта 2010

Вы не можете создавать экземпляры неполного типа (компилятор ничего не знает о классе!)

Вам необходимо переместить определения ваших функций (конструктор A и B) в файл C ++, который может включать оба заголовка (или в несколько файлов C ++, если вы придерживаетесь соглашения, что у вас есть один класс на файл).

Как уже было сказано, у вашего написанного кода есть серьезная проблема: каждый A создает экземпляр B, а каждый B создает и экземпляр A. У вас будет бесконечная рекурсия, и в конечном итоге вам не хватит памяти.

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

2 голосов
/ 30 марта 2010

РЕДАКТИРОВАТЬ: Прочитайте Джеймс МакНеллис ответ в первую очередь - это пример кода того, что вам нужно сделать. Но рекурсия - это более важный вопрос, и он заслуживает каких-либо голосов за этот конкретный вопрос, а не я :)

Вы не можете использовать встроенные функции здесь, так как полное определение для классов A и B недоступно, когда вы объявляете их встроенными. Объявите их как обычные функции, и вы будете в порядке с вашими предварительными объявлениями.

classA.h

#ifndef _class_a_h_
#define _class_a_h_

#include "classB.h"

class B; //????

class A
{
public:
    A();
    virtual ~A();
    int num_a;
    B* ptr_b;
};

#endif //_class_a_h_

classB.h

#ifndef _class_b_h_
#define _class_b_h_

#include "classA.h"

class B
{
public:     
    B();
    virtual ~B();
    int num_b;
    A* ptr_a;
};

#endif //_class_b_h_

classes.cpp

#include "classA.h"
#include "classB.h"

A::A() {
    ptr_b = new B(); //????
}

A::~A() {
    if(ptr_b) delete ptr_b; //????
}

B::B() { 
    ptr_a = new A; //????
}

B::~B() { 
    if(ptr_a) delete ptr_a; //????
}
1 голос
/ 30 марта 2010

classB.h: в конструкторе 'B :: B ()':

classB.h: 12: ошибка: недопустимое использование неполного типа 'struct A'

A не полностью определено.Вы только дали ему прототип (class A;).

classB.h: 6: ошибка: прямое объявление 'struct A'

classB.h: в деструкторе'virtual B :: ~ B ()':

Я думаю это та же проблема.Ему нужно знать, как определяется A, чтобы знать, сколько памяти нужно освободить.

Выполните рефакторинг кода, чтобы удалить циклическую зависимость.(A создает B, а B создает A ... создает B, создает A, создает B ...)

0 голосов
/ 30 марта 2010

Простое решение состоит в том, чтобы вытянуть определения функций-членов из строки в файлы classA.cpp и classB.cpp. Удалите взаимные включения из заголовочных файлов и вместо этого вставьте их в файлы .cpp.

Везде, где класс A или B используется больше, чем просто по имени (то есть, кроме просто именования указателей или ссылочных типов), полное описание класса уже должно присутствовать. С вашим текущим включенным / встроенным дизайном одно или другое не обязательно будет завершенным. Разбивая реализации классов на файлы .cpp, вы позволяете объявлениям завершаться изящно перед созданием объектов типа A или B.

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