2013-03-28 4 views
0

Мои поиски заставили меня поверить, что проблема, которую я испытываю, называется циклической избыточностью. Я не понимаю ни одного из выпущенных решений. Я (довольно) новичок в C++, исходя из сильного фона Java.Классы C++, зависимые друг от друга, вызывающие ошибку циклической зависимости

В принципе, существуют два класса, которые зависят друг от друга. Класс A содержит вектор объектов класса B, а класс B содержит методы, для которых объекты класса A являются входными.

Вот код, который воспроизводит проблему.

В соответствии с коделитом g ++ ошибка находится в school.h и «человек не был объявлен в этой области». Он также говорит, что «аргумент шаблона 1 недействителен» и «аргумент шаблона № 2 недействителен». Затем пара других, о неклассовом типе «int» для всех векторных функций, которые вызываются.

main.cpp

#include <iostream> 
#include <string> 
#include "person.h" 
#include "school.h" 

int main() { 
    person p; 
    school s; 
    std::cout << p.name << std::endl; 
    s.personVect.push_back(p); 
    std::cout << s.personVect.size() << std::endl; 
    std::cout << s.personVect.at(0).name << std::endl; 
    p.test(); 
    return 0; 
} 

school.h

#ifndef SCHOOL_H 
#define SCHOOL_H 
#include <vector> 
#include "person.h" 

class school 
{ 
public: 
    school(); 
    ~school(); 

    std::vector<person> personVect; 


}; 

#endif // SCHOOL_H 

school.cpp

#include "school.h"  
school::school(){}  
school::~school(){} 

person.h

#ifndef PERSON_H 
#define PERSON_H 
#include <string> 
#include <vector> 
#include "school.h" 


class person { 
public: 

    std::string name; 
    std::string phone; 

    school doSomethingWithSchool(); 

    void test(); 

    person(); 
    ~person(); 

}; 

#endif // PERSON_H 

person.cpp

#include "person.h" 
#include <iostream> 
using namespace std; 

person::person() 
{ 
    name = "marcus"; 
    phone = "0400000000"; 
} 

person::~person() 
{ 
} 

void person::test() { 
    cout << this->name; 
} 
school person::doSomethingWithSchool() { 
    school s; 
} 
+0

Вы забыли задать вопрос. «Что мне делать, если мне нужна эта функциональность?»? – Vesper

ответ

0

Вместо того, в том числе человека #include "person.h"
В заголовочном файле ("school.h") просто написать class person; и использовать указатель на person
И в C++ модуль ("school.cpp") включают в себя «person.h», если вам нужно.

Это также будет иметь некоторые преимущества в функции. (Уменьшить время компиляции)

Полезные ссылки:

http://www.codeproject.com/Articles/547275/Whyplusisplusitplusbuildingplussopluslong-3f https://stackoverflow.com/a/553869/328260

+0

Это не сработает. Поскольку он использует экземпляры, а не указатели – Eran

+0

Да, спасибо. У меня есть вопрос. – azat

3

Проблема может быть решена путем разработки вам класса отношений лучше.

A Person не состоит из School, поэтому ему не требуется член школы.

A School имеет коллекцию Person объектов.

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

// Person.h 
class School; // Forward declare school type. 

// Person.cpp 
Person::DoSomethingWithSchool(School* school); 
0

Попробуйте это,

людей.ч

 #ifndef PERSON_H 
     #define PERSON_H 
     #include <string> 
     #include <vector> 

     class school; 
     class person { 
     public: 

      std::string name; 
      std::string phone; 

      school doSomethingWithSchool(); 

      void test(); 

      person(); 
      ~person(); 

     }; 

     #endif // PERSON_H 

school.h

 #ifndef SCHOOL_H 
     #define SCHOOL_H 
     #include <vector> 

     class person; 
     class school 
     { 
     public: 
      school(); 
      ~school(); 

      std::vector<person*> personVect; 
     }; 

     #endif // SCHOOL_H 

person.cpp

#include "person.h" 
    #include "school.h" 
    #include <iostream> 
    using namespace std; 

    person::person() 
    { 
     name = "marcus"; 
     phone = "0400000000"; 
    } 

    person::~person() 
    { 
    } 

    void person::test() { 
     cout << this->name; 
    } 
    school person::doSomethingWithSchool() { 
     school s; 
     return s; 
    } 

и в main.cpp

int main() 
    { 
     person *p = new person; 
     school s; 
     std::cout << p->name << std::endl; 
     s.personVect.push_back(p); 
     std::cout << s.personVect.size() << std::endl; 
     std::cout << s.personVect.at(0)->name << std::endl; 
     p->test(); 
     delete p; 
     return 0; 
    } 
0

Ваш вопрос с тем, что school.h имеет оператор #include "person.h", а person.h имеет #include "scho ol.h ".

Раствор

В school классе, вместо vector<person>, используйте vector<person*>. Вместо включения person.h здесь добавьте заявление class person;. Предполагая, что ваш класс school в конечном итоге изменит объекты person, поместите #include person.h в ваш файл school.cpp после заявления #include school.h.

Ошибка объяснил

Это разбивка шаг за шагом, что происходит при компиляции:

  1. В main.cpp, вы первый включают person.h. Препроцессор отправляется и находит person.h. Поскольку вы никогда не включали его раньше, PERSON_H еще не определен, и ifndef PERSON_H в настоящее время истинно. Итак, переходим к следующей строке в файле.

  2. Первый Интересная вещь, которая происходит в этом файле, заключается в том, что PERSON_H определяется (#fdefine PERSON_H). В следующий раз вы получите этот файл, препроцессор будет пропускать весь файл, прямо к #endif.

  3. второй интересной вещью является то, что школа входит в комплект. Поэтому препроцессор находит school.h (мы еще не обработали класс школы!), Который до сих пор не обрабатывался.

  4. Первая интересная вещь в school.h (после #define SCHOOL_H) является #include person.h, так препроцессор возвращает к person.h (мы еще не обработали класс Person или!). Но на этот раз PERSON_H уже определен, поэтому #ifndef PERSON_H является ложным, а препроцессор пропускает #endif в этом файле (как я упоминал в (2)). Таким образом, класс Person еще не был объявлен.

  5. Вернитесь в школу.h, ничего не делая. На этом этапе мы собираемся объявить/определить школьный класс. Класс человека имеет никогда не был объявлен, потому что наш препроцессор прыгнул с человека.h в школу.h обратно на person.h обратно в школу.h, обрабатывая только #include директивы.

  6. Компилятор начинает определение класса school. Следующая интересная вещь - линия std::vector<person> personVect;. Вы пытались создать экземпляр класса vector<person>. Но person здесь неизвестно (т. Е. Не было объявлено в этой области) из-за (5).Это объясняет ваши два аргумента аргумента шаблона: A vector<person> фактически неявно является vector <person, allocator<person> >. Первый аргумент шаблона - person, а второй - allocator<person>. Поскольку person неизвестно, оба этих аргумента недействительны.

Решение объяснил

Тот факт, что school.h включает person.h и person.h включает school.h, что вызывает циклическую зависимость, вы намекало. Чтобы этого избежать, нам нужен альтернативный способ объявить, что мы будем использовать определенный класс в файле. Общий подход заключается в использовании декларации - заявлении class person;, о котором я упомянул в решении, до определения school. Это обычное объявление, которое позволяет компилятору узнать, что person (как используется в классе school) относится к классу.

Однако для того, чтобы построить vector<person>, компилятор также должен знать, сколько места в памяти класса person занимает (т.е. его размер), который мы не обеспечиваем (и никогда не сможет вручную предоставить) с декларацией class person; , Фактически, компилятор знает только размер класса после обработки полного определения в файле заголовка. Но если мы попытаемся это сделать, мы вернемся к квадрату (циклическая зависимость, а что нет). То, что мы знаем, это размер указатель до person, так как это зависит от машины (например, на 32-битной машине, размер указателя на что-либо - 32 бита). Следовательно, мы можем создать экземпляр класса vector<person*>, поскольку это всегда вектор 32-битных объектов, независимо от того, что на самом деле есть person.