Абсолютно. Модель сборки C / C ++ - это ... хм ... анахронизм (если говорить лучше). Для крупных проектов это становится серьезным PITA.
Как правильно заметил Нил, это должно не быть подходом по умолчанию для вашего дизайна класса, не уходите с дороги, если вам действительно не нужно.
Разрывной циркуляр включает ссылки - это единственная причина, по которой вы должны использовать предварительные декларации.
// a.h
#include "b.h"
struct A { B * a; }
// b.h
#include "a.h" // circlular include reference
struct B { A * a; }
// Solution: break circular reference by forward delcaration of B or A
Сокращение времени восстановления - Представьте себе следующий код
// foo.h
#include <qslider>
class Foo
{
QSlider * someSlider;
}
теперь каждый файл .cpp, который прямо или косвенно извлекает файл Foo.h, также извлекает файл QSlider.h и все его зависимости. Это могут быть сотни .cpp файлов! (Предварительно скомпилированные заголовки помогают немного - а иногда и очень - но они превращают давление диска / процессора в память / давление диска и, таким образом, вскоре достигают «следующего» предела)
Если заголовок требует только ссылочного объявления, эту зависимость часто можно ограничить несколькими файлами, например, foo.cpp.
Сокращение добавочного времени сборки - Эффект еще более выражен при работе с собственными (а не стабильными библиотеками) заголовками. Представь, что у тебя есть
// bar.h
#include "foo.h"
class Bar
{
Foo * kungFoo;
// ...
}
Теперь, если большинству ваших .cpp нужно добавить bar.h, они также косвенно добавят foo.h. Таким образом, каждое изменение foo.h запускает сборку всех этих файлов .cpp (которые могут даже не знать Foo!). Если вместо этого bar.h использует прямое объявление для Foo, зависимость от foo.h ограничивается bar.cpp:
// bar.h
class Foo;
class Bar
{
Foo * kungFoo;
// ...
}
// bar.cpp
#include "bar.h"
#include "foo.h"
// ...
Это настолько распространено, что это шаблон - шаблон PIMPL . Его использование двоякое: во-первых, оно обеспечивает истинную изоляцию интерфейса / реализации, другое - уменьшение зависимостей сборки. На практике я бы оценил их полезность 50: 50.
Вам нужна ссылка в заголовке, вы не можете иметь прямую реализацию зависимого типа. Это ограничивает случаи, когда могут применяться предварительные декларации. Если вы делаете это явно, для этого обычно используется служебный класс (например, boost :: scoped_ptr ).
Стоит ли время сборки? Определенно , я бы сказал. В худшем случае время сборки вырастает полиномом с количеством файлов в проекте. другие методы, такие как более быстрые машины и параллельные сборки, могут обеспечить только процентное увеличение.
Чем быстрее сборка, тем чаще разработчики тестируют то, что они делали, тем чаще выполняются модульные тесты, тем быстрее могут быть исправлены разрывы сборки, и реже разработчики в конечном итоге откладывают.
На практике управление временем сборки, хотя оно и необходимо для большого проекта (скажем, для сотен исходных файлов), оно все же создает «разницу в комфорте» в небольших проектах. Кроме того, добавление улучшений по факту часто является упражнением в терпении, так как одно исправление может сбить только секунды (или меньше) 40-минутной сборки.