• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

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

  • Автор темы Monarh
  • Дата начала
M

Monarh

Здравствуйте!

Есть следующий код:
В первом модуле 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.

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

rrrFer

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

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

1.h:

2.h:
Код:
#include "1.h"

Т.е. если препроцессор первым делом дойдет до 1.h, то он получит следующий код в 1.h:
Код:
#include "1.h"

Получит он такой код и сойдет с ума. Используй директивы условной компиляции типа #ifdef или сразу #progma once (но лучше, все таки, лучше #ifdef)



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

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

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

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

struct Somestruct; // для тех, что с первого раза не понял, видимо.

Добавлено: Ну и напоследок,

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

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

Monarh

Используй директивы условной компиляции типа #ifdef или сразу #progma once
Я их итак использую, здесь писать их, для краткости, не стал.
Использую их одновременно: #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
struct SomeStruct {
int a, b;
};
struct Somestruct; // для тех, что с первого раза не понял, видимо.
И это Я прекрасно понимаю!

Однако, при компиляции 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;
Если Мои рассуждения неправильны, то поправьте.

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

rrrFer

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

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

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

Monarh

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

rrrFer

Хотя, конечно, эти предварительные декларации - это костыли!

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

нельзя вложить в класс А экземпляр неполного класса В. Неполного, т.к. в классе В ты пытаешься использовать класс А.
Ты можешь положить туда указатели или ссылки, но не использовать их.

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

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

Добавлено: И все же я проблемы не вижу, если в классе В ты не используешь тип, вложенный в А.
Ты пробовал делать то, что я советовал?
Как Я уже несколько раз писал, всё работает если в первом модуле A.hpp отсутствуют функции с вложенными типами, например,
если функция объявлена так:
std::vector<double> funcA( const Cls_B& ) const;
но нужно с вложенным типом:
std::vector<double> funcA( const Cls_B& , const Cls_B::TFEPos& ) const;
А при этом, как написано в первом посте:

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

Код:
#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

Код:
#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

Это код работает, в чем проблема? - и как ты видишь, тут нет лишней предварительной декларации и лишнего инклуда.
 
M

Monarh

Ай да шаман, ай да 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
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!