Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > C/C++: Общие вопросы > Forward Declaration / Forward Reference |
Автор: SABROG 21.4.2009, 19:27 | ||||
Вопрос теоретический. Еще раз напомню особенности для Forward Declaration. Без включения #include можно объявлять ссылки или указатели на объект любого класса таким образом:
Я специально убрал #include, где прописан тип MyType, чтобы как-бы повысить скорость компиляци. Но в итоге то .cpp файл не будет компилиться, т.к. ему все-равно нужно все знать о типе MyType. Вопрос в том, как тогда нужно сделать рефакторинг модулей, чтобы и #include убрать с объявлением типа и при этом не прописывать новый #include в .cpp файле. Я логику подобной оптимизации по скорости компиляции никак не уловлю. Или для такой структуры программы оптимизация не нужна в принципе, т.к. нечего оптимизировать - одно последовательное включение. А если хедер будет использоваться сразу в 10 .cpp файлах, неужели лучше (для скорости компиляции) прописать в каждом из десяти файлов - #include с объявлением MyType, вместо того, чтобы это сделать один раз в общем заголовочном файле? |
Автор: zim22 21.4.2009, 19:31 |
не хотите все заголовки впихнуть в stdafx.h установить в опциях компилятора Create/Use Precompiled Header и потом в .cpp файле в первой строчке кода делать инклуд stdafx.h ? |
Автор: SABROG 21.4.2009, 20:46 |
Я использую gcc. Насчет redunant include guards, precompiled headers, pragma once, multicore compilation и distribute compilation - осведомлен. Меня сейчас интересует именно вопрос с forward declaration. |
Автор: SABROG 21.4.2009, 21:53 | ||
Ну так то вроде бы от перемены мест слагаемых сумма не меняется. А вот что будет быстрее для компилятора - 10 раз прочитать с винчестера 10 5кб-ных файлов или считать 50кб одним махом. Мне почему-то кажется второе. Например, если копировать сотни мелких файлов в винде это долгая операция в отличае от копирования одного файла, скажем .zip архива с нулевой степенью сжатия, где будут находится все эти файлы. Только у меня еще остается вопрос. Если поменять что-то в одном таком общем хедере, то перекомпиляция будет для всех .cpp файлов в которые он включен? А это уже будет обратный эффект тому ради чего создавался этот общий хедер. |
Автор: mes 21.4.2009, 22:04 | ||||
Как понял Вы думаете, что если хидеры включены в хидере, а не в .сpp они один раз за всю компиляцию считываютаются. неа.. точно также заново для каждой единицы трансляции (при оговоренном Вами выше условии, об отстствии прекомпиляции) к тому же если тянут лишние хидеры, то в сумме получается выше и по кол-ву считываемыех файлов и по объему текста для парсера. ну а если хотите съэкономить на считывании то добавьте второю пару стражей
![]() |
Автор: SABROG 21.4.2009, 22:16 |
Ну со стражами проблем у меня нет. Это наглядный пример redunant include guards (ну или pragma once). Но, если возвращаться к forward declaration, то как должна выглядеть структура модулей, чтобы это благоприятно сказывалось на скорости компиляции (т.е. если забыть о первичном назначении forward declaration и сосредоточиться на побочном эффекте невключения лишних заголовков)? |
Автор: J0ker 21.4.2009, 22:35 |
а с какого перепугу скорость компиляции должна увеличиться? |
Автор: mes 21.4.2009, 22:44 | ||||||||
Судя по высказыванию Вы не разобрались с примером. Никто не говорил что у Вас проблемы со стражами. Просто при обычном(одинарном) использовании страж, вначале считывается файл, потом проверяется включен ли он уже. А при двойном, как в примере выше, вначале проверяется включен ли уже такой файл и если не включен, то тогда происходит считывание. Так что эффект от подобного использования есть и в инете я помойму даже видел сравнительную таблицу.
Все как и обычно, только в хидеры ничего лишнего не подключать. ну и так как создавать объекты "вперед объявленнoго" класса нельзя, то следовательно, для случая
придется приглашать указатели и new/delete
Добавлено @ 22:49 самое интересное что скорость увеличивается ![]() точнее сказать время компиляции уменьшается ![]() |
Автор: Alek86 21.4.2009, 22:58 |
дык с какого перепугу? forward declarations нужны для уменьшения связи между модулями то есть при изменении одного ашника в большом проекте, использущем forward declarations нужно будет перекомпиливать меньше модулей, чем в том же проекте, в котором бездумно понапиханы инклуды для увеличения скорости компиляции используются precompiled headers как они помогают увеличивать скорость, я не очень понимаю наверное компилер при создании прекомпилед pdb файла приводит их в какой-то полускомпиленный (для шаблонов) вид, который помогает ускорять инстанцирование |
Автор: mes 21.4.2009, 23:09 | ||
меньше модулей подключено -> меньше считывается+меньше парсится.
http://www.gamearchitect.net/Articles/ExperimentsWithIncludes.html сам я тоже убедился на личном опыте, у меня в проекте было примерно 100 файлов и после тщательной проверки инклудов со применением forward declaration время сократилось в почти два раза, точные значения шас не помню, но примерно вместо 2 минут 20 стал компилится 1 min 20. да от этого выгода намного заметнее (так как чaстичная перекомпиляция намного чаще чем полная ![]() ![]() |
Автор: Alek86 21.4.2009, 23:35 |
а, точно, еще парсинг тогда да, есть выгода хотя странно, что такая огромная |
Автор: mes 21.4.2009, 23:45 |
Ну а какой ей быть если вместо n нужных строк приходится компилить k*n строк ![]() все зависит от кол-ва включенного лишнего кода ![]() |
Автор: SABROG 22.4.2009, 00:03 |
Если эти 2 строки разделить по модулям .h и .cpp. То ошибка будет при компиляции .cpp файла, т.к. тип A нигде не определен, а следовательно #include с объявлением A таким образом просто переносится из .h файла в .cpp файл(ы). Так ведь? А тогда где выйгрыш? До: ![]() После: ![]() P.S.: по UML также как и по C++ у меня 3 с натяжкой ![]() |
Автор: mes 22.4.2009, 00:16 | ||
В нарисованном вами случаем нигде. А теперь представьте для 100 хидеров. Ведь сппшнику необходимы далеко не все файлы. Тогда как первом случае в каждый cpp-шних будут включены в каждый все 100, а во 2м также по 2-4 хидера ![]() Чтоб лучше представить, на 2й вашей картинке только две линии про cpp-юнит оставьте. |
Автор: J0ker 22.4.2009, 00:43 |
абсолютно соглаен, что выигрыш в парсинге никакого выигрыша в самой компиляции не получится просто не ожидал такой большой разницы... |
Автор: SABROG 22.4.2009, 08:32 | ||||
Теперь я понял, как-раз о такой структуре модулей я вопрашал создавая эту тему. Т.е. единственный правильный вариант при котором можно добиться оптимизации ![]()
А разве выйгрыш в скорости парсинга как следствие не дает выйгрыш в скорости компиляции? |
Автор: mes 22.4.2009, 08:51 | ||||
Да.. только не обязательно чтоб от cppшника была только одна линия. Главное, чтоб не было включено ничего лишнего. И из за "необработки" это лишнего и будет экономия ![]() Добавлено через 5 минут и 24 секунды
тут игра слов, зависит от точки зрения, что именно считать компиляцией. Весь этап от вместе с чтением, парсингом и препроцессором или чисто компиляцию. И к тому же _скорость_ компиляции остается вообще без изменения. Изменяется объем компиляции и, как следствие, _время_ компиляции. ![]() |
Автор: SABROG 22.4.2009, 09:15 | ||
Значит, если говорить об "общем времени компиляции", то выйгрыш будет? Должен ведь быть ![]() |
Автор: Earnest 22.4.2009, 09:17 |
Освобождение от лишних зависимостей (включая использование forward declarations вместо полных инклудов) - это очень хорошая привычка, полезность которой весьма заметна на больших проектах. Если у вас три с половиной файла, которые сильно между собой связаны, то разницы вы не увидите. Но привычку надо вырабатывать - будут и большие проекты. Сильные зависимости плохи не только для скорости компиляции (в конце концов, мощность машин растет). Гораздо сильнее они мешают при внесении изменений в дизайн. Поэтому нужно просто привыкнуть делать все так локально как только возможно. Кроме прочего, получите еще и лучший дизайн, если избавитесь от привычки подключать все, что в голову пришло. |
Автор: mes 22.4.2009, 09:49 | ||
![]() Earnest, ![]() |
Автор: math64 22.4.2009, 10:03 | ||
|
Автор: J0ker 22.4.2009, 16:18 | ||
ну тут и свои минусы есть например в VS "Go to Definition" отправит вас к forward declaration, а не туда куда нужно |
Автор: zim22 22.4.2009, 16:27 | ||
а если Visual Assist подключить? |
Автор: J0ker 22.4.2009, 17:55 | ||||
не знаю не пользовался, но подозреваю что не поможет, т.к. это не глюк, а формально правильное поведение - forward declaration и реалюное объявление - формально разные типы |
Автор: Любитель 22.4.2009, 19:08 |
Как раз правильней различать Declaration и Definition ;) |
Автор: J0ker 22.4.2009, 19:32 | ||
все претензии к VS - в случае не включения хедеров в проект она видит только forward declaration в обоих случаях (что, впрочем, и понятно) |
Автор: Alek86 22.4.2009, 19:41 |
J0ker, помогает сначала идет к объявлению, а по второму нажатию - к определению |
Автор: Любитель 22.4.2009, 19:46 |
Я не против. Просто я к тому, что это логически не правильно - это баг строо говоря. |
Автор: J0ker 22.4.2009, 19:49 | ||||
только если у вас оба хедера включены (прямо или косвенно) в проект, и InteliSense еще не переклинило (что на больших и не очень проектах бывает с ним довольно часто) Добавлено через 14 минут и 27 секунд
не понял где баг? я хочу посмотреть на определение и жму на "Go to Definition" а попадаю на forward declaration я-то тут при чем |
Автор: Любитель 22.4.2009, 20:23 |
Баг VS, а не |
Автор: Alek86 22.4.2009, 20:46 | ||
а как ты себе представляешь использование класса, если его хедер нигде не включен в проекте? |
Автор: J0ker 22.4.2009, 21:01 | ||
вот такой я любопытный ![]() |
Автор: math64 23.4.2009, 09:39 | ||
Класс определён в dll, а в проекте только forward declaration |
Автор: Alek86 23.4.2009, 10:23 |
math64, и как компилер скомпилит обращение к какой-либо паблик функции этого класса, без ашника? |
Автор: math64 23.4.2009, 10:39 |
Классы А и В - dll, A* a - private поле в классе В. Класс C в проекте использует класс B, инкюдит заголовок В, в котором только предварительное объявление class A; Работа с классом A - только через public методы класса B |
Автор: J0ker 23.4.2009, 16:02 |
или просто lib или даже obj |
Автор: kamre 23.4.2009, 17:34 | ||
http://en.wikipedia.org/wiki/Opaque_pointer? |
Автор: Alek86 23.4.2009, 23:05 | ||
правда тогда ты вообще знаешь о существовании класса A только из-за его объявления в заголовке B возможно, разработчики ассиста посчитали лишней тратой времени пытаться искать не нужный разработчику класс A по всему солюшену (кстати, его определения в солюшене может и не оказаться, а может оказаться несколько разных) |