Круговые зависимости объявлений - PullRequest
5 голосов
/ 17 ноября 2009

Я пытаюсь реализовать пример шаблона посетителя, но у меня проблема с циклическими зависимостями объявлений классов. Когда я делаю предварительное объявление класса Visitor, классы Россия и Англия не знают, что у Visitor есть метод посещения, но когда я продлеваю предварительное объявление класса Visitor для метода accept, мне нужно использовать классы Англия и Россия, но они должны знать, кто Посетитель, потому что они используют этот тип в своем коде. Я пробовал много вариантов упорядочения кода, но полностью провалился. Пожалуйста, помогите мне понять, что нужно С ++ для этого. Спасибо.

#include <cstdio>
#include <vector>

using namespace std;

class Visitor;

class Land {
  public:
    virtual void accept(const Visitor *v);
};

class England : public Land {
  public:
    void accept(const Visitor *v) {
      v->visit(this);
    }
};

class Russia : public Land {
  public:
    void accept(const Visitor *v) {
      v->visit(this);
    }
};

class Visitor {
  public:
    void visit(const England *e) const {
      printf("Hey, it's England!\n");
    }

    void visit(const Russia *r) const {
      printf("Hey, it's Russia!\n");
    }
};

class Trip {
  private:
    vector<Land> *l;
  public:
    explicit Trip(vector<Land> *_l):l(_l) {}
    void accept(Visitor *v) {
      for (unsigned i = 0; i < l->size(); i++) {
        l->at(i).accept(v);
      }
    }
};

int main() {
  England england;
  Russia russia;
  vector<Land> trip_plan;
  trip_plan.push_back(england);
  trip_plan.push_back(russia);
  trip_plan.push_back(england);
  Trip my_trip(&trip_plan);
  Visitor me;
  my_trip.accept(&me);
  return 0;
}

А есть вывод g ++

c++ -ansi -Wall -Wextra -Wconversion -pedantic -Wno-unused-parameter  -o vp vp.cc
vp.cc: In member function ‘virtual void England::accept(const Visitor*)’:
vp.cc:40: error: invalid use of incomplete type ‘const struct Visitor’
vp.cc:30: error: forward declaration of ‘const struct Visitor’
vp.cc: In member function ‘virtual void Russia::accept(const Visitor*)’:
vp.cc:47: error: invalid use of incomplete type ‘const struct Visitor’
vp.cc:30: error: forward declaration of ‘const struct Visitor’

Ответы [ 6 ]

8 голосов
/ 17 ноября 2009
class Visitor; 

class England : public Land {
  public:
    void accept(const Visitor *v); // Only declaration
};


// Define Visitor
class Visitor {
  //...
};

// Now implementation
void England::accept(const Visitor *v) {
      v->visit(this);
}
1 голос
/ 01 мая 2012

Когда вы пересылаете объявление, компилятор C ++ знает, что существует пользовательский тип такого типа, но он не знает о его членах и методах данных. Чтобы использовать все возможности этого пользовательского типа, вам необходимо включить его заголовочный файл, в котором расположены все его методы и члены данных, прежде чем использовать их, в противном случае вы просто делаете предварительное объявление и используете его методы и члены данных там, где находится его заголовочный файл включен. В вашем случае вы вызываете метод visit () объявленного вперед класса Visitor, таким образом вы сообщаете компилятору, что существует тип данных Visitor, но компилятор еще не знает о методе visit (). Чтобы решить эту проблему, вы должны удалить предварительную декларацию и поместить определение Visitor поверх всех классов. У вас будет что-то вроде этого

#include <cstdio>
#include <vector>

using namespace std;
class England;
class Russia;
class Visitor {
  public:
    void visit(const England *e) const {
      printf("Hey, it's England!\n");
    }

    void visit(const Russia *r) const {
      printf("Hey, it's Russia!\n");
    }
};

class Land {
  public:
    virtual void accept(const Visitor *v);
};

class England : public Land {
  public:
    void accept(const Visitor *v) {
      v->visit(this);
    }
};

class Russia : public Land {
  public:
    void accept(const Visitor *v) {
      v->visit(this);
    }
};
...
1 голос
/ 17 ноября 2009

Ответ Алексея Малистова решает вашу проблему. Это просто также открывает следующую проблему.

Компилятор gcc жалуется на vtable (который, помимо прочего, используется для классов с виртуальными функциями). Используемые правила документированы (см. документы ):

Если класс объявляет любые не встроенные, не чистые виртуальные функции, первая из них выбирается в качестве «ключевого метода» для класса, а vtable генерируется только в модуле перевода, где определен ключевой метод.

Теперь версия вашего класса Алексея определяет не встроенную, не чистую виртуальную функцию accept . Таким образом, gcc откладывает создание экземпляра Land vtable, пока не увидит определение Land :: accept . Добавьте это и посмотрите, работает ли это. Или, как говорит Николаз, просто сделайте его виртуальным.

Ну, я не хочу "решать" проблему. Я хочу понять, что не так и почему

Привыкайте отделять объявления от определений. За исключением особого случая шаблонов, C ++ работает лучше таким образом.

1 голос
/ 17 ноября 2009

Алексей уже дал одну часть ответа.

Тем не менее, если вы не собираетесь внедрять команду accept for Land, вам нужно:

class Land {
  public:
    virtual void accept(const Visitor *v)= NULL;
};
0 голосов
/ 17 ноября 2009

Дайте все объявления класса класса перед его использованием .. Я думаю, что это будет работать.

0 голосов
/ 17 ноября 2009

Я давно не писал сложные программы на C ++, но если я правильно помню, вам следует выложить скелет этих классов в файл .h с тем же именем и этим файлом .c. Затем включите его в этот .c файл.

Надеюсь, это поможет.

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