Ввод Нового Имени Для Типа В Одном Классе И Использование Его В Другом

Тема в разделе "Общие вопросы по С и С++", создана пользователем Monarh, 24 июн 2013.

Наш партнер Genesis Hackspace
  1. Monarh

    Monarh Active Member

    Регистрация:
    14 фев 2009
    Сообщения:
    34
    Симпатии:
    0
    Здравствуйте!

    Есть следующий код:
    В первом модуле A.hpp
    Код (C++):
    #include "B.hpp"
    class Cls_B;

    class Cls_A {
    // Функции:
    public:
    std::vector<double> funcA( const Cls_B& , const Cls_B::TFEPos& ) const;
    };
    Во втором модуле B.hpp
    Код (C++):
    #include "A.hpp"
    class Cls_A;

    class Cls_B {
    // Типы:
    public:
    typedef std::vector< std::vector< double >::const_iterator > TFEPos;

    // Функции:
    public:
    void funcB( const Cls_A& );
    };
    В итоге:
    Первый модуль компилируется на ура, а
    второй со следующими ошибками:
    1>A.hpp(108): error C2027: использование неопределенного типа "Cls_B"
    1>A.hpp(22): см. объявление "Cls_B"
    1>A.hpp(108): error C4430: отсутствует спецификатор типа - предполагается int. Примечание. C++ не поддерживает int по умолчанию
    1>A.hpp(108): error C2143: синтаксическая ошибка: отсутствие "," перед "&"

    A.hpp(22) указывает на строчку
    class Cls_B;

    A.hpp(108) указывает на строчку
    std::vector<double> funcA( const Cls_B& , const Cls_B::TFEPos& ) const;
    проблема в типе Cls_B::TFEPos.

    Подскажите, пожалуйста, как решить эту проблему ?
    Заранее, всем ответившим, премного благодарен!
     
  2. rrrFer

    rrrFer Гость

    Директива include очень тупая.
    На фазе препроцессорной обработки, которая удивишься какая первая, строка с include заменяется содержимым файла.

    Дак вот что делать программе:

    1.h:
    2.h:
    Код (Text):
    #include "1.h"
    Т.е. если препроцессор первым делом дойдет до 1.h, то он получит следующий код в 1.h:
    Код (Text):
    #include "1.h"
    Получит он такой код и сойдет с ума. Используй директивы условной компиляции типа #ifdef или сразу #progma once (но лучше, все таки, лучше #ifdef)



    Добавлено: Но то, что выше - было вступлением.

    Так то у тебя в коде вобще ниче не понятно.

    Ты понимаешь зачем нужна предварительная декларация на подобии "class Cls_B;" ? - правильно, чтобы не писать #include "b.h"

    Дак нахрена ты сначала пишешь #include "b.h", который уже включает ОПИСАНИЕ класса B в файл, а потом еще выполняешь предварительную декларацию? - она при этом вобще никуда не играет, это примерно как написать:
    Добавлено: Ну и напоследок,

    предварительная декларация не поможет тебе в этих случаях:
    const Cls_B&, и тем более в этих: const Cls_B::TFEPos&

    Предварительная декларация как бэ говоорит компилятору, "учти, что есть такой тип как..., но этот тип будет реализован где-то дальше, не ругайся в этом месте, если он используется". И компилятор послушно неругается, если ему ВСЕ ПОНЯТНО, т.е. если в этом месте вобще неважно какой тип используется (у тебя заведен указатель на экземпляр). А в твоем случае ниризу не указатель, компилятор должен знать сколько памяти выделить под объекты, ну а во втором случае (const Cls_B::TFEPos&
    ) - знать еще больше (тут ты вобще начинается использовать этот Cls_:)
     
  3. Monarh

    Monarh Active Member

    Регистрация:
    14 фев 2009
    Сообщения:
    34
    Симпатии:
    0
    Я их итак использую, здесь писать их, для краткости, не стал.
    Использую их одновременно: #pragma once, для тех компиляторов, которые его понимают,
    #ifdef для тех, которые #pragma once не понимают.
    Обычно пишу так (доп. предварительные декларации только для перекрёстно зависимых классов):
    В первом модуле A.hpp
    Код (C++):
    #pragma once

    #ifndef A_HPP
    #define A_HPP

    #include "B.hpp"
    class Cls_B;

    class Cls_A {
    // Функции:
    public:
    std::vector<double> funcA( const Cls_B& , const Cls_B::TFEPos& ) const;
    };

    #endif A_HPP
    Во втором модуле B.hpp
    Код (C++):
    #pragma once

    #ifndef B_HPP
    #define B_HPP

    #include "A.hpp"
    class Cls_A;

    class Cls_B {
    // Типы:
    public:
    typedef std::vector< std::vector< double >::const_iterator > TFEPos;

    // Функции:
    public:
    void funcB( const Cls_A& );
    };

    #endif B_HPP
    И это Я прекрасно понимаю!

    Однако, при компиляции A.cpp, без предварительные декларации class Cls_B;
    компилятор ругается так:
    1>B.hpp(143): error C4430: отсутствует спецификатор типа - предполагается int. Примечание. C++ не поддерживает int по умолчанию
    1>B.hpp(143): error C2143: синтаксическая ошибка: отсутствие "," перед "&"
    B.hpp(143) указывает на строчку
    void funcB( const Cls_A& );
    при компиляции B.cpp ошибки полностью аналогичные, компилятор ругается на
    std::vector<double> funcA( const Cls_B& , const Cls_B::TFEPos& ) const;

    После добавления во второй модуль предварительной декларации
    class Cls_A;
    первый модуль успешно компилируется.
    А также, после добавления в первый модуль предварительной декларации
    class Cls_B;
    второй модуль успешно компилируется, если без Cls_B::TFEPos.
    Далее всё нормально работает.
    Как сделать с Cls_B::TFEPos незнаю, в этом и вопрос.
    Проблема именно в перекрёстной зависимости.

    Далее просто попытка объяснить успешность предварительной декларации.
    Что происходит при компиляции A.cpp, как это Я понимаю:
    Код (C++):
    #pragma once
    #ifndef A_HPP
    #define A_HPP - определяется A_HPP
    #include "B.hpp" - включается содержимое "B.hpp", развернём его в следующем коде
    class Cls_B;
    class Cls_A {
    // Функции:
    public:
    std::vector<double> funcA( const Cls_B& , const Cls_B::TFEPos& ) const;
    };
    #endif A_HPP
    Разворачиваем "B.hpp"
    Код (C++):
    #pragma once
    #ifndef A_HPP
    #define A_HPP - определяется A_HPP
    #pragma once
    #ifndef B_HPP
    #define B_HPP - определяется B_HPP
    #include "A.hpp" - предполагается включение содержимого "A.hpp", но, поскольку, A_HPP уже определено, то повторно он не включается!
    class Cls_A;        - для этого доп. декларация, сам класс Cls_A, как Мы видим, описан ниже

    class Cls_B {
    // Типы:
    public:
    typedef std::vector< std::vector< double >::const_iterator > TFEPos;

    // Функции:
    public:
    void funcB( const Cls_A& ); - компилятор понимает этот код благодаря доп. декларации, без неё эта строчка ему непонятна!
    };

    #endif B_HPP

    class Cls_B;    - эта строчка, при компиляции A.cpp, игнорируется, поскольку класс Cls_B уже объявлен
    class Cls_A {
    // Функции:
    public:
    std::vector<double> funcA( const Cls_B& , const Cls_B::TFEPos& ) const; - класс Cls_B уже объявлен и эта строчка компилятору понятна
    };
    #endif A_HPP
    модуль успешно компилируется!

    Что происходит при компиляции B.cpp, как это Я понимаю:
    Код (C++):
    #pragma once
    #ifndef B_HPP
    #define B_HPP - определяется B_HPP
    #include "A.hpp" - включается содержимое "A.hpp", развернём его в следующем коде
    class Cls_A;

    class Cls_B {
    // Типы:
    public:
    typedef std::vector< std::vector< double >::const_iterator > TFEPos;

    // Функции:
    public:
    void funcB( const Cls_A& );
    };
    #endif B_HPP
    Разворачиваем "A.hpp"
    Код (C++):
    #pragma once
    #ifndef B_HPP
    #define B_HPP - определяется B_HPP
    #pragma once
    #ifndef A_HPP
    #define A_HPP - определяется A_HPP
    #include "B.hpp" - предполагается включение содержимого "B.hpp", но, поскольку, B_HPP уже определено, то повторно он не включается!
    class Cls_B;        - для этого доп. декларация, сам класс Cls_B, как Мы видим, описан ниже

    class Cls_A {
    // Функции:
    public:
    std::vector<double> funcA( const Cls_B& , const Cls_B::TFEPos& ) const; - компилятор понимает первый аргумент благодаря доп. декларации, но не второй
    };
    #endif A_HPP

    class Cls_A;    - эта строчка, при компиляции B.cpp, игнорируется, поскольку класс Cls_A уже объявлен

    class Cls_B {
    // Типы:
    public:
    typedef std::vector< std::vector< double >::const_iterator > TFEPos;

    // Функции:
    public:
    void funcB( const Cls_A& ); - класс Cls_A уже объявлен и эта строчка компилятору понятна
    };
    #endif B_HPP
    Если в классе объявлена функция не std::vector<double> funcA( const Cls_B& , const Cls_B::TFEPos& ) const;
    а std::vector<double> funcA( const Cls_B& ) const;
    то модуль успешно компилируется!
    но нужно именно std::vector<double> funcA( const Cls_B& , const Cls_B::TFEPos& ) const;
    Если Мои рассуждения неправильны, то поправьте.

    Как решить эту проблему с вложенным типом или с перекрёстной зависимостью?
     
  4. rrrFer

    rrrFer Гость

    дак есть проблема? - вроде бы работает все.

    include "a.h" можно обще убрать

    предваритальную декларацию B тоже убрать.
     
  5. Monarh

    Monarh Active Member

    Регистрация:
    14 фев 2009
    Сообщения:
    34
    Симпатии:
    0
    Проблема есть!
    Как Я уже несколько раз писал, всё работает если в первом модуле A.hpp отсутствуют функции с вложенными типами, например,
    если функция объявлена так:
    std::vector<double> funcA( const Cls_B& ) const;
    но нужно с вложенным типом:
    std::vector<double> funcA( const Cls_B& , const Cls_B::TFEPos& ) const;
    А при этом, как написано в первом посте:
    Здесь Ты что-то не-то говоришь.
    Если Мы уберём #include "A.hpp" из второго модуля, то как компилятор узнает что такое Cls_A и как с ним работать?
    декларация class Cls_A; говорит только о том, что Cls_A - пользовательский тип, о его структуре компилятор ничего знать, в таком случае, не будет.
    Можно, конечно, включить #include "A.hpp" в файле B.cpp, но он и здесь не мешает
    Я предпочитаю делать так:
    если имеется в описании класса (hpp-файле) ссылки на какой-то модуль, то подключать этот модуль в hpp-файле,
    если же только в реализации класса имеются ссылки на какой-то модуль, то подключать этот модуль в cpp-файле.
    Обрати внимание на описание "Что происходит при компиляции B.cpp"
    там она необходима.
    Хотя, конечно, эти предварительные декларации - это костыли!
    Чувствуется Мне, что надо как-то по-другому это разруливать, но как не знаю.
    Поэтому и спрашиваю.
     
  6. rrrFer

    rrrFer Гость

    Предварительная декларация - полезная штука, снижающая время сборки проекта.
    По возможности в хедерах размещаются только предварительный декларации, include помещаются только в единицы компиляции
    нельзя вложить в класс А экземпляр неполного класса В. Неполного, т.к. в классе В ты пытаешься использовать класс А.
    Ты можешь положить туда указатели или ссылки, но не использовать их.

    Cls_B::TFEPos& // тут ты пытаешься использовать неполный класс.

    У тебя что-то с архитектурой не то.

    Добавлено: И все же я проблемы не вижу, если в классе В ты не используешь тип, вложенный в А.
    Ты пробовал делать то, что я советовал?
    В классе А используется тип, влодженный в В, но в В - нет - тогда это нормальная ситуация, проблемы вобще нет.

    Код (Text):
    #ifndef A_H
    # define A_H
    # include "b.h"
    class Cls_A {
    public:
    void funcA( const Cls_B& , const Cls_B::TFEPos& ) const {}
    };
    #endif // A_H
    Код (Text):
    #ifndef B_H
    # define B_H
    # include <vector>
    class Cls_A;
    class Cls_B {
    public:
    typedef std::vector< std::vector< double >::const_iterator > TFEPos;
    public:
    void funcB( const Cls_A& ) {}
    };
    #endif // B_H
    Это код работает, в чем проблема? - и как ты видишь, тут нет лишней предварительной декларации и лишнего инклуда.
     
  7. Monarh

    Monarh Active Member

    Регистрация:
    14 фев 2009
    Сообщения:
    34
    Симпатии:
    0
    Ай да шаман, ай да r04! :)
    Действительно работает! :D
    Огромное спасибо!

    Я и не вкладываю.
    У Меня же не так написано:
    Код (C++):
    class Cls_A {
    Cls_B B;
    Cls_B::TFEPos Pos;
    };
    а так:
    Код (C++):
    class Cls_A {
    public:
    void funcA( const Cls_B& , const Cls_B::TFEPos& ) const;
    };
    Это разные вещи.

    А если понадобится в обоих классах использовать вложенные типы другого класса, то как тогда разрулить эту ситуацию ?
    То есть, например, в такой ситуации:
    Код (C++):
    #ifndef A_H
    # define A_H
    # include "b.h"
    # include <set>
    class Cls_A {
    public:
    typedef std::set< double > TCoord;
    public:
    void funcA( const Cls_B& , const Cls_B::TFEPos& ) const {}
    };
    #endif // A_H
    Код (C++):
    #ifndef B_H
    # define B_H
    # include <vector>
    # include "a.h"
    class Cls_B {
    public:
    typedef std::vector< std::vector< double >::const_iterator > TFEPos;
    public:
    void funcB( const Cls_A&, const Cls_A::TCoord& ) {}
    };
    #endif // B_H
     
Загрузка...

Поделиться этой страницей