Опережающее объявление класса
Программирование › Программирование на С++ › Учебные материалы по С++ › Опережающее объявление класса
Помечено: cplusplus, forward-reference class, объявление класса, опережающее объявление, предварительное объявление, программирование
В этой теме 1 ответ, 2 участника, последнее обновление 2 года/лет, 5 мес. назад.
- АвторСообщения
- 04.09.2016 в 00:21 #3041@questioner
В исходном коде к статье «Работа с сетью в Qt. Паттерн Адаптер» вместо
#include <QTcpServer>
используется записьclass QTcpServer;
в заголовочном файле (.h
), однако в соответствующем файле реализации (.cpp
) потом все равно содержится директива#include
. Зачем это делается?# include <QObject> # include <QList> class QTcpServer; class ISocketAdapter; class Server : public QObject { Q_OBJECT public: explicit Server(int nPort, QObject *parent = 0); public slots: void on_newConnection(); void on_disconnected(); void on_message(QString); protected: QTcpServer* m_ptcpServer; QList<ISocketAdapter*> m_clients; };
В этом же примере почему то не пишетсяclass QList
и т.п., почему? - 04.09.2016 в 01:29 #3042@admin
Прием, используемый в этом примере называется предварительным (опережающим) объявлением класса (forward reference class). Такое объявление помогает решить, как минимум, две проблемы:
1 Ослабить зависимости между файлами, сократить время компиляции
Директива
#include
говорит компилятору, что он должен включить код одного файла в другой файл. Это сложная операция, которая может занимать много времени. С другой стороны, допустим, мы изменили что-либо в классе — при следующей компиляции проекта будут перекомпилированы все файлы, в которые включался"server.h"
. В больших проектах такая компиляция может заниматься несколько часов.
В ряде случаев компиляции всех файлов, использующих изменяемый класс можно избежать. Посмотрите на классServer
— он содержит указатель на классQTcpServer
, т.е. вне зависимости от того, как реализованQTcpServer
, наш класс будет содержать, скажем, 4 байта с адресом объекта. Во всех таких случаях мы можем (нам следует) не включать заголовочный файл целиком, а лишь указать компилятору, что такой класс есть в нашей программе при помощи опережающего объявления. Это же относится к списку указателей —QList<ISocketAdapter*>
, а также к всем случаям, когда класс содержит (или его функция принимает в качестве аргументов) указатель или ссылку на объект.
В связи с этим, Скотт Мэйерс рекомендует избегать использования объектов, если можно обойтись ссылками или указателями. Однако, надо помнить, что использование объектов в классе, в отличии от указателей, безопасно с точки зрения исключений — поэтому чтобы ослабить зависимости и при этом не получить проблем с исключениями — используйте какой-либо вид умных указателей.2 Разорвать циклические зависимости классов
Допустим, у нас получилась следующая ситуация:
// файл foo.h #include "bar.h" class Foo { Bar bar; }; // файл bar.h #include "foo.h" class Bar { f(Foo foo); };
Ситуация безусловно не самая лучшая, однако ее вариации иногда встречаются на практике — зависимости между файлами могут создаваться аргументами функций-членов. Компилятор не сможет обработать эти файлы, т.к. между ними есть циклические зависимости. Значит, нам надо ослабить эти зависимости, например так:// файл foo.h #include "bar.h" class Foo { Bar bar; }; // файл bar.h class Foo; class Bar { f(const Foo &foo); }
Рекомендую прочитать подробнее про указатели и ссылки — ведь с их помощью можно не только ослабить зависимости, но и повысить гибкость. Кроме того, использование объектов-членов в классах нарушает принцип инверсии зависимостей (DIP — Dependency Inversion Principle).
- АвторСообщения
Для ответа в этой теме необходимо авторизоваться.