Недостатки форвардной декларации?

В С++ и Objective-C я привык к переходу - объявлению любых необходимых классов, которые не нужно определять в заголовке, а затем импортировать файлы заголовков, определяющие эти классы в исходных файлах, если это необходимо.

Есть ли такая ситуация, когда это не будет хорошей идеей?

(Я знаю, что большой недостаток форвардной декларации - ограниченное использование неполного типа. В целях этого вопроса предположим, что в заголовке мне нужно использовать только что объявленный вперед класс как неполный тип.)

Ответ 1

Иногда вы можете изменить семантику программы тонко, не поднимая никаких ошибок

class Foo;

template < typename T>
struct Trait
{
    static const int MY_TYPE = -1;
};

// Lets say this is Foo.h
//class Foo
//{
//};
//template<>
//struct  Trait<Foo>
//{
//  static const int MY_TYPE = 1;
//};

void TestFunction(Foo& f)
{
    std::cout << Trait<Foo>::MY_TYPE << std::endl;
}

Рассмотрим приведенный выше код, а код с комментариями живет в заголовке. Если заголовок включен, TestFunction будет печатать 1 в противном случае -1

Ответ 2

Одним из недостатков может быть отсутствие скрытия/инкапсуляции информации.

Я предпочитаю иметь самый минимальный заголовочный файл, который я могу. Это означает, что я не обязан продолжать поддерживать множество функций. Если я хочу что-то изменить, и что-то не было открыто в публичном заголовке, есть гораздо больше шансов, что я могу изменить его внутри класса, не затрагивая никого.

EDIT:

Люк попросил пример, так что я пойду:

Предположим, что у вас есть класс под названием Car. И единственное, на что вы его построили, - это добраться от точки A до точки B. Меня лично, я бы предпочел сохранить мой заголовочный файл: класс под названием Car и метод под названием Drive. По тому, как вы сформулировали свой вопрос ( "все классы, которые я могу" ), я ожидал бы найти такие классы, как "DieselEngine", "PetrolEngine", "HybidEngine" и т.д. В вашем файле заголовка. Проблема в том, что другие люди, работающие над вашим проектом (или, вы, со временем), начинают использовать эти открытые классы. Теперь, два года спустя, вы решили "Hrm... что класс PetrolEngine действительно вызывает проблемы для меня. Я думаю, что я просто удалю его и заменим его на HybridEngine полностью в моем классе автомобилей" - ну, теперь PetrolEngine включен в 100 других файлов по причинам, которые вы не понимаете, и теперь вы вынуждены поддерживать PetrolEngine (и работать так, как раньше) для всех тех парней, которые использовали его, в некоторых говорят, что вы не совсем уверены в - потому что у вас не было прочного рабочего "контракта", поскольку вы хотели, чтобы этот класс использовался в первую очередь. Это была деталь реализации того, что вы действительно хотели выполнить - создание автомобиля.

EDIT для обсуждения комментариев о скрытии информации:

Если все, что вы делаете, строго форвардно объявляет имя класса/структуры - ну, я думаю, я снова спросил бы "почему". Если я являюсь потребителем вашего файла заголовка и класса (ов), и я не могу ничего сделать с этим классом - и он не отображается как параметр или возвращаемый тип вашего API-интерфейса основного класса, то зачем его вообще открывать

Если вы используете его для проверки типа времени компиляции для непрозрачных структур данных - отлично - это одна вещь. Но, как вы сформулировали свой вопрос, это звучало так, как будто все было в файле заголовка, как само собой разумеющееся.

Ответ 3

Передовые объявления могут применяться ко многим вещам: классам, функциям, глобальным переменным/константам (с использованием extern) и в С++ 11, перечислениях.

Основным недостатком, насколько я вижу, является избыточность, поскольку избыточность вводит больше возможностей для ошибок, однако это очень сильно отличается в зависимости от того, что вы декларируете вперед.

Если у вас есть ошибка с прямым объявлением класса, глобальным или перечислением, компилятор должен забрать его (да!).

Если у вас есть ошибка с объявлением функции, то в С++ вы просто создали перегрузку (oups!).

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

Ответ 4

Нет, насколько я знаю. Единственный недостаток, который вы уже рассмотрели, - это использование неполных типов. Поскольку вы говорите, что не будет никаких обычаев, которые нуждаются в полностью определенных типах, вы в порядке, я думаю.

Ответ 5

Форвардные объявления предпочтительнее включения полного файла, если вам не требуется полное определение класса в заголовке. Единственный минус, о котором я могу думать, уже был размещен в качестве комментария от @Stephen Darlington, который я бы дал 10 upvotes, если бы мог.:)

Ответ 6

Одним из недостатков является потенциальная утечка памяти в форвардных объявленных типах. Подробнее см. этот вопрос. В этом случае компиляторы Microsoft выдают warning C4150: deletion of pointer to incomplete type 'SomeType'; no destructor called.

Ответ 7

Возможно, это, скорее всего, вопрос читаемости для ваших коллег, который будет продолжать работу по кодированию.