5 / 5 / 0
Регистрация: 24.08.2011
Сообщений: 191
|
||||||||||||||||
1 | ||||||||||||||||
Как избежать циклического включения заголовочных файлов?12.12.2014, 12:41. Показов 11133. Ответов 10
Метки нет (Все метки)
Сделал в проге кучу классов и кучу указателей на них. В итоге при компиляции выдает кипу ошибок:
0
|
12.12.2014, 12:41 | |
Ответы с готовыми решениями:
10
Про добавление заголовочных файлов в заголовочных файлах Как избежать многократного включения модуля с помощью #include в проект? Как посмотреть содержимое заголовочных файлов в Си? Как компилятору указать директорию заголовочных файлов. |
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,270
|
|
12.12.2014, 14:37 | 3 |
Defake, у тебя циклическое включение заголовочных файлов (Object.h включает Act.h, а Act.h включает Object.h). Убери из Object.h включение Act.h.
0
|
5 / 5 / 0
Регистрация: 24.08.2011
Сообщений: 191
|
||||||||||||||||
12.12.2014, 19:36 [ТС] | 4 | |||||||||||||||
DrOffset, для этого и существует #pragma once. А если не включать Act.h в Object, то как его использовать? Но, собственно, убрал уже, т.к. понял, что там я акт не использую)) Но цикличности много еще кроме этого
Kerry_Jr, ну, файл Act.h уже в первом сообщении (самый последний) Далее, файл Actor.h: Кликните здесь для просмотра всего текста
Файл Location.h: Кликните здесь для просмотра всего текста
Так, ну и Menu.h: Кликните здесь для просмотра всего текста
0
|
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,270
|
||||||
12.12.2014, 20:07 | 5 | |||||
Defake, эта тема наверное уже в миллионный раз здесь возникает.
Нет, она для предотвращения включения дважды и более одного и того же файла в одну и ту же единицу трансляции. Магическим образом твои циклические включения разруливаться от этого не будут. Нужно понять что есть заголовочный файл и как его правильно использовать. И что такое единица трансляции в С++. А еще что такое предварительное объявление. А пока что, я покажу простой пример на одном из твоих файлов, дальше, очень надеюсь, ты справишься сам
Вот одна из последних тем, там есть пример в развернутом виде, который показывает что происходит при циклических include`ах.
1
|
5 / 5 / 0
Регистрация: 24.08.2011
Сообщений: 191
|
|||||||||||
12.12.2014, 21:30 [ТС] | 6 | ||||||||||
DrOffset, да, сейчас путем коммента всего и потом раскомментирования по кусочкам сам это увидел, вы были правы)
И еще пару вопросов: 1) В примере моего кода вы оставили:
И если я буду использовать методы, нельзя вообще никак обойтись без .cpp? Просто неудобно очень)
0
|
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,270
|
||||||
12.12.2014, 21:56 | 7 | |||||
Сообщение было отмечено Defake как решение
Решение
Можно, но это уже будет нетривиально и зависеть от конкретной ситуации.
Разносить определения и объявления функций в классе в любом случае придется. Смотри. Чтобы использовать тип, он должен быть объявлен. Мы это обеспечили через forward declaration. Избавились от зависимостей в виде заголовочных файлов в данном месте. Но. Совсем от них избавиться нельзя, потому что чтобы производить операции с объектами типа нужна знать его полное определение. Поэтому в месте, где нужно полное определение типа (создание объекта, вызов методов), все-таки их надо включить. Если два класса используют друг друга (что в принципе само по себе не очень, но это вопрос отдельный), то единственный способ разорвать рекурсию, это разнести использование друг друга по разным единицам трансляции, т.е. разным cpp. Это удобно. Есть такое довольно известное правило проектирования классов. Заключается оно в том, что по началу все методы реализуются в cpp, а в h остается только интерфейс. И только по результатам профилирования можно некоторые функции перенести в качестве inline в h. Это позволяет сделать код более локальным (без лишних зависимостей). Более локальный код всегда проще отлаживать. На самом деле ты можешь не переносить все методы в cpp. Если задача позволяет избавиться от перекрестных включений без этого. Добавлено через 11 минут Я не увидел у тебя в коде использования объектов по этим указателям
1
|
5 / 5 / 0
Регистрация: 24.08.2011
Сообщений: 191
|
|
12.12.2014, 22:21 [ТС] | 8 |
DrOffset, просто первый язык программирования мой был ActionScript, это было в те времена, когда я еще почти ничего не знал о программинге, так что привычки и привязанности появились именно там. И там было всё как раз в одном файле.
Про переменные могли не объяснять второй раз, я просто уточнил, можно ли использовать их как возвращаемое значение методов. Про неправильную структуру кстати да.. Буду думать, как исправить) Какое огромное облегчение, что необязательно все методы засовывать в cpp, а только те, которые используют другие классы) Спасибо за помощь!
0
|
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,270
|
|
12.12.2014, 23:50 | 9 |
А, я видимо просто вопроса не понял. Если это указатель или ссылка (или контейнер указателей), то можно. Правило в общем простое, forward declaration достаточно всегда, если не требуется знание о размере объекта этого типа. В случае с указателями и ссылками, размер не требуется (указатель на любые данные в С++ имеет размер sizeof(void*)), поэтому это работает. Если ты возвращаешь объект по значению, то тут нужен размер, а также знание о конструкторах и деструкторах, в этом случае потребуется уже полное определение типа, forward declaration недостаточно.
0
|
13.12.2014, 00:04 | 10 | ||||||||||||||||||||
Для лучшего понимания объясню применимость предварительного объявления и инклюдов.
Для чего оба они нужны - в общем-то понятно. Когда компилятор видит в текстовом файле исходника слово MyClass, он должен понять, что это именно класс. Для этого он предварительно должен увидеть объявление этого класса - либо предварительное
Когда требуется полное объявление? Обычно - тогда, когда мы создаем переменную этого класса (то же самое - передача аргумента функции по значение, возврат из функции по значению). В этих случаях нам необходимо знать размер такой переменной, чтобы выделить подходящее количество памяти. Простое предварительное объявление такой информации не содержит. В полном же объявлении указаны все переменные-члены, все функции-члены, а также другие атрибуты, которые могут влиять на размер переменной данного типа (например, наличие виртуальных методов требует добавления в переменную указателя на vtable, который тоже занимает определенное количество памяти). Поэтому полное объявление, напротив, предоставляет всю необходимую информацию, чтобы высчитать sizeof для объектов данного класса. Другой случай, когда предварительным объявлением не обойтись - когда вы вызываете функцию-член данного класса. Понятно, что компилятор должен вообще убедиться, что у класса есть функция-член с подходящей сигнатурой. А вот когда нам вполне достаточно предварительного объявления. В сущности, только в одном случае - когда мы объявляем указатель или ссылку на объект данного класса. Размер указателя один и тот же, независимо от типа объекта, на который он ссылается, компилятору в этот момент достаточно информации о том, что это "указатель на что-то". Ровно то же справедливо и для ссылок. Однако, если вы вызываете по указателю/ссылке функцию-член, или обращаетесь по нему/ней к переменной-члену, то в силу вступает предыдущий абзац, и вам уже снова будет нужно полное объявление. Небольшой пример на тему:
Ну кроме, конечно, шаблонов, но с ними совершенно отдельный разговор - по-другому просто не получится. К сожалению, разные языки требуют разных подходов. Одна из особенностей C++ - разделение класса по крайней мере на два файла - *.h и *.cpp. В некоторых случаях возможно поместить все в один файл, но это будет плохой код на C++. В C# или в Pascal/Delphi, например, код тоже не делится на два файла. Это особенность этих языков. Хотя, конечно, "программист на фортране может писать программы на фортране на любом языке", но если вы хотите создавать хороший код, то к каждому языку нужен отдельный подход. P.S. Ну вот, опередили)
2
|
DrOffset
|
13.12.2014, 00:13
Как избежать циклического включения заголовочных файлов?
#11
|
0
|
13.12.2014, 00:13 | |
Раздельная компиляция (нюансы использования заголовочных файлов и файлов реализации) не разберусь как переработать программу с учетом использования заголовочных файлов, модулей и пользовательских функций Универсальный способ подключения заголовочных файлов, или как мне не ловить цикличное обьявление каждую компиляцию? Подключение заголовочных файлов и файлов реализации Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |