Вопрос

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

При этом я столкнулся с двумя проблемами:

  1. Форвардное объявление базовых классов не работает.

    class B;
    
    class A : public B
    {
    
        // ...
    }
    
  2. Форвардное объявление для классов STD не работает.

    namespace std
    {
        class string;
    }
    
    class A
    {
        string aStringToTest;
    }
    

Как мне решить эти проблемы?

Это было полезно?

Решение

Первая проблема, которую вы не можете решить.

Вторая проблема не имеет ничего общего с классами стандартной библиотеки.Это потому, что вы объявляете экземпляр класса как член своего собственного класса.

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

Однако компилятор может определить размер указателя на класс, даже если у него еще нет его полного определения.Таким образом, возможным решением в таких случаях является наличие члена-указателя (или ссылки) в классе-потребителе.

В случае с базовым классом это не поможет, потому что вы не получите отношения «является».

И не стоит делать что-то вроде std::string.Во-первых, это должна быть удобная оболочка вокруг символьного буфера, избавляющая вас от необходимости управлять памятью в чем-то столь простом.Если вы затем удерживаете на него указатель, просто чтобы не включать заголовок, вы, вероятно, зашли слишком далеко.

Во-вторых (как указано в комментарии), std::string является определением типа для std::basic_string<char>.Поэтому вместо этого вам нужно объявить (а затем использовать) это, и к этому времени все становится очень неясным и трудным для чтения, что является еще одним видом затрат.Действительно ли оно того стоит?

Другие советы

Как уже ответил Эрвикер, вы не можете использовать упреждающие объявления ни в одном из этих случаев, поскольку компилятору необходимо знать размер класса.

Вы можете использовать предварительное объявление только в наборе операций:

  • объявление функций, которые принимают объявленный класс в качестве параметров или возвращают его
  • объявление указателей членов или ссылок на заранее объявленный класс
  • объявление статических переменных типа, объявленного вперед, в определении класса

Вы не можете использовать его для

  • объявить атрибут-член данного типа (компилятор требует размера)
  • определить или создать объект типа или удалить его
  • вызвать любой статический метод или метод-член класса или получить доступ к любому члену или статическому атрибуту

(я что-нибудь забыл?)

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

Есть и другие тонкости.Когда вы объявляете класс, вы сообщаете компилятору, что это будет класс.Это означает, что оно не может быть enum или typedef в другой тип.Это проблема, с которой вы сталкиваетесь, когда пытаетесь переслать объявление std::string, поскольку это определение типа конкретного экземпляра шаблона:

typedef basic_string<char> string; // aproximate

Чтобы переслать строку объявления, вам нужно будет переслать объявление basic_string шаблон, а затем создайте typedef.Проблема в том, что в стандарте не указано количество параметров, которые basic_string шаблон принимает, он просто указывает, что если он принимает более одного параметра, остальные параметры должны иметь тип по умолчанию, чтобы выражение выше компилировалось.Это означает, что не существует стандартного способа объявления шаблона.

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

template <typename T, typename U> class Test; // correct
//template <typename T> class Test; // incorrect even if U has a default type

template <typename T, typename U = int> class Test {
   // ...
};

В конце совет, который вам дал Родди:вперед объявите столько, сколько сможете, но предполагайте, что некоторые вещи должны быть включены.

Вы слишком стараетесь решить что-то, что на самом деле не является проблемой.Используйте нужные вам заголовочные файлы и уменьшите - ГДЕ ВОЗМОЖНО - потребность в них.Но не пытайтесь доводить дело до крайности, иначе вы потерпите неудачу.

В некоторых случаях вам может помочь идиома PIMPL, но не здесь.

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

Упреждающее объявление классов STL часто нецелесообразно, поскольку реализации обычно включают явные экземпляры шаблонов, которые ускоряют компиляцию.

Для ваших базовых классов вам необходимо иметь полное определение типа, а не только объявление.Заголовки производных типов должны будут включать #include заголовки своих базовых классов.

Для классов в пространстве имен std вы должны включить правильный заголовок — в данном случае <string> — а затем выполнить одно из трёх действий:

  1. Полностью определите тип:Std :: String AstringTotest

  2. Поместите заявление только для этого типа:используя std::string;

  3. Вставьте в объявление для пространства имен STD:использование пространства имен std;

> Кажется, что предварительное объявление бесполезно для базовых классов и классов stl.

Исправление...Форвардное объявление является НЕПОДХОДИМЫМ для базовых классов и членов объекта.(Это не «бесполезно», это «неприменимо».)

Базовый класс ДОЛЖЕН быть объявлен (не объявлен заранее), когда он объявляется как базовый класс другого класса.

Член объекта ДОЛЖЕН быть объявлен (не объявлен вперед) при объявлении другим классом, как параметр или как возвращаемое значение.ПРИМЕЧАНИЕ:по ссылке или по указателю не имеет такого ограничения.

Исправление...Предварительное объявление классов STL - согласно ISO 14882 - неопределенное поведение.http://www.gotw.ca/gotw/034.htm

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top