Я ищу определение, когда мне разрешено делать предварительное объявление класса в заголовочном файле другого класса:
Могу ли я сделать это для базового класса, для класса, который является членом, для класса, переданного функции-члену по ссылке и т. Д.?
c++
forward-declaration
c++-faq
Игорь Окс
источник
источник
Ответы:
Поставьте себя в положение компилятора: когда вы пересылаете объявление типа, все, что знает компилятор, это то, что этот тип существует; он ничего не знает о его размере, членах или методах. Вот почему он называется неполным типом . Следовательно, вы не можете использовать тип для объявления члена или базового класса, поскольку компилятор должен знать макет типа.
Предполагая следующую предварительную декларацию.
Вот что вы можете и не можете сделать.
Что вы можете сделать с неполным типом:
Объявите член указателем или ссылкой на неполный тип:
Объявите функции или методы, которые принимают / возвращают неполные типы:
Определите функции или методы, которые принимают / возвращают указатели / ссылки на неполный тип (но без использования его членов):
Что вы не можете сделать с неполным типом:
Используйте это как базовый класс
Используйте его, чтобы объявить участника:
Определите функции или методы, используя этот тип
Используйте его методы или поля, фактически пытаясь разыменовать переменную с неполным типом
Что касается шаблонов, то здесь нет абсолютного правила: можно ли использовать неполный тип в качестве параметра шаблона, зависит от способа использования типа в шаблоне.
Например,
std::vector<T>
требует , чтобы его параметр был полным типом, в то времяboost::container::vector<T>
как нет. Иногда полный тип требуется только в том случае, если вы используете определенные функции-члены; это касаетсяstd::unique_ptr<T>
, например.Хорошо документированный шаблон должен указывать в своей документации все требования его параметров, включая то, должны ли они быть полными типами или нет.
источник
Основное правило заключается в том, что вы можете только объявить вперед классы, чей макет памяти (и, следовательно, функции-члены и члены-данные) не обязательно должен быть известен в файле, который вы объявите в нем.
Это исключило бы базовые классы и все, кроме классов, используемых через ссылки и указатели.
источник
Лакос различает использование классов
Я никогда не видел, чтобы это произносили более кратко :)
источник
Помимо указателей и ссылок на неполные типы, вы также можете объявить прототипы функций, которые указывают параметры и / или возвращаемые значения, которые являются неполными типами. Однако вы не можете определить функцию, имеющую параметр или тип возврата, который является неполным, если это не указатель или ссылка.
Примеры:
источник
Ни один из ответов пока не описывает, когда можно использовать предварительное объявление шаблона класса. Итак, вот и все.
Шаблон класса может быть передан, объявлен как:
Следуя структуре принятого ответа ,
Вот что вы можете и не можете сделать.
Что вы можете сделать с неполным типом:
Объявите член указателем или ссылкой на неполный тип в другом шаблоне класса:
Объявите элемент указателем или ссылкой на один из его неполных экземпляров:
Объявите шаблоны функций или шаблоны функций-членов, которые принимают / возвращают неполные типы:
Объявите функции или функции-члены, которые принимают / возвращают один из его неполных экземпляров:
Определите шаблоны функций или шаблоны функций-членов, которые принимают / возвращают указатели / ссылки на неполный тип (но без использования его членов):
Определите функции или методы, которые принимают / возвращают указатели / ссылки на один из его неполных экземпляров (но без использования его членов):
Используйте его как базовый класс другого шаблонного класса
Используйте его для объявления члена другого шаблона класса:
Определите шаблоны функций или методы, используя этот тип
Что вы не можете сделать с неполным типом:
Используйте один из его экземпляров в качестве базового класса
Используйте один из его экземпляров, чтобы объявить члена:
Определите функции или методы, используя один из его экземпляров
Используйте методы или поля одного из его экземпляров, фактически пытаясь разыменовать переменную с неполным типом
Создать явные экземпляры шаблона класса
источник
X
иX<int>
точно такая же, и только декларативный синтаксис каким-либо существенным образом отличается, причем все, кроме 1 строки вашего ответа, равняются только принятию Люка иs/X/X<int>/g
? Это действительно нужно? Или я пропустил крошечную деталь, которая отличается? Это возможно, но я несколько раз визуально сравнивал и ничего не вижу ...В файле, в котором вы используете только указатель или ссылку на класс. И никакая функция-член / член не должна вызываться с помощью этих указателей / ссылок.
с
class Foo;
// предварительным объявлениемМы можем объявить элементы данных типа Foo * или Foo &.
Мы можем объявлять (но не определять) функции с аргументами и / или возвращаемыми значениями типа Foo.
Мы можем объявить статические данные-члены типа Foo. Это связано с тем, что члены статических данных определены вне определения класса.
источник
Я пишу это как отдельный ответ, а не просто как комментарий, потому что я не согласен с ответом Люка Турайля не из-за законности, а из-за надежного программного обеспечения и опасности неправильного толкования.
В частности, у меня есть проблема с подразумеваемым договором о том, что пользователи вашего интерфейса должны знать.
Если вы возвращаете или принимаете ссылочные типы, то вы просто говорите, что они могут проходить через указатель или ссылку, которые они, в свою очередь, могут знать только через предварительное объявление.
Когда вы возвращаете неполный тип,
X f2();
вы говорите, что у вызывающей стороны должна быть полная спецификация типа X. Им это нужно для создания LHS или временного объекта на сайте вызова.Точно так же, если вы принимаете неполный тип, вызывающая сторона должна создать объект, который является параметром. Даже если этот объект был возвращен из функции как еще один неполный тип, сайту вызова необходимо полное объявление. то есть:
Я думаю, что есть важный принцип, что заголовок должен предоставлять достаточно информации, чтобы использовать его без зависимости, требующей других заголовков. Это означает, что заголовок должен быть включен в модуль компиляции, не вызывая ошибки компилятора при использовании любых функций, которые он объявляет.
Кроме
Если это внешняя зависимость желаемого поведения. Вместо использования условной компиляции у вас может быть хорошо задокументированное требование предоставить им собственный заголовок, объявляющий X. Это альтернатива использованию #ifdefs и может быть полезным способом представить макеты или другие варианты.
Важным отличием является то, что некоторые шаблонные методы, в которых вы явно НЕ должны создавать их экземпляры, упоминаются просто для того, чтобы кто-то не стал меня раздражать.
источник
I disagree with Luc Touraille's answer
Так что напишите ему комментарий, в том числе ссылку на пост в блоге, если вам нужна длина. Это не отвечает на заданный вопрос. Если бы все думали о том, как работает Х, оправданные ответы, не соглашаясь с тем, как это делает Х, или обсуждая пределы, в которых мы должны ограничивать нашу свободу использования Х - у нас почти не было бы реальных ответов.Основное правило, которому я следую, - не включать заголовочный файл, если это не нужно. Поэтому, если я не сохраню объект класса как переменную-член своего класса, я не буду его включать, я просто буду использовать предварительное объявление.
источник
Пока вам не нужно определение (подумайте об указателях и ссылках), вы можете отказаться от предварительных объявлений. Вот почему в основном вы будете видеть их в заголовках, в то время как файлы реализации обычно вытягивают заголовок для соответствующих определений.
источник
Обычно вы хотите использовать предварительное объявление в заголовочном файле классов, когда вы хотите использовать другой тип (класс) в качестве члена класса. Вы не можете использовать заранее объявленные методы классов в заголовочном файле, потому что C ++ пока не знает определения этого класса. Это логика, которую вы должны перемещать в .cpp-файлы, но если вы используете функции-шаблоны, вы должны уменьшить их до той части, которая использует шаблон, и переместить эту функцию в заголовок.
источник
Предположим, что предварительное объявление получит ваш код для компиляции (объект obj создан). Однако связывание (создание exe) не будет успешным, пока не найдены определения.
источник
class A; class B { A a; }; int main(){}
и дайте мне знать, как это происходит. Конечно, это не скомпилируется. Все правильные ответы здесь объяснить , почему и точные, ограниченные условия , в которых предобъявление является действительным. Вместо этого вы написали это о чем-то совершенно ином.Я просто хочу добавить одну важную вещь, которую вы можете сделать с перенаправленным классом, не упомянутым в ответе Люка Турэя.
Что вы можете сделать с неполным типом:
Определите функции или методы, которые принимают / возвращают указатели / ссылки на неполный тип и передают эти указатели / ссылки на другую функцию.
Модуль может проходить через объект объявленного вперед класса другому модулю.
источник
Как, Люк Торайль уже очень хорошо объяснил, где использовать, а не использовать предварительное объявление класса.
Я просто добавлю к этому, почему мы должны использовать это.
Мы должны по возможности использовать объявление Forward, чтобы избежать нежелательного внедрения зависимости.
Поскольку
#include
файлы заголовков добавляются в несколько файлов, поэтому, если мы добавим заголовок в другой файл заголовка, это добавит нежелательную инъекцию зависимостей в различные части исходного кода, чего можно избежать, добавляя#include
заголовок в.cpp
файлы везде, где это возможно, вместо добавления в другой файл заголовка и используйте декларацию форварда класса везде, где это возможно, в заголовочных.h
файлах.источник