Создайте предварительное объявление класса, и эти зависимости компиляции исчезнут. Использование предварительного объявления просто создает имя, на которое можно ссылаться далее в заголовочном файле. Компоновщик должен будет сам найти в файлах реализаций подходящее определение.
К несчастью, использовать предварительное объявление можно не всегда. Класс Вв примере 2.4 использует только указатели или ссылки на A, так что ему достаточно только предварительного объявления. Однако если бы в определении класса Вя использовал функцию-член (метод) или переменную Аили если бы создавал объект типа А, а не только указатель или ссылку на него, то предварительного объявления окажется недостаточно. Причиной этого является то, что файлы, включающие myheader.h , должны знать размер В, и если Aявляется членом В, то компилятор, чтобы определить размер В, должен знать размер А. Указатель или ссылка на что-либо всегда имеют один и тот же размер, так что в случае использования указателей или ссылок подробности об Акомпилятор не интересуют, и, следовательно, заголовочный файл не требуется
Неудивительно, что если включить в myheader.h какое-либо определение, использующее членов A, то потребуется включить через #include заголовок для А. Это требуется для того, чтобы компилятор мог проверить сигнатуру используемой функции-члена Аили тип переменной-члена А. Вот иллюстрация кода, требующего #include.
#include "a.h"
class B {
public:
void f(const A& a) {
foo_ = a.getVal(); // требуется знать, допустимо ли a.getVal
}
}
// ...
В общем случае используйте предварительное объявление тогда, когда это позволяет снизить количество #include, что отражается на времени компиляции.
2.4. Предотвращение конфликта имен с помощью пространств имен
Проблема
В несвязанных между собой модулях обнаружены конфликтующие имена или требуется заранее избежать возможности таких конфликтов, создав логические группы кода.
Решение
Для структурирования кода используйте пространства имен. С помощью пространств имен можно объединять большие группы кода, находящиеся в разных файлах, в единое пространство имен. Для разбиения больших модулей на подмодули можно использовать вложенные пространства имен, и потребители вашего модуля смогут выборочно открывать элементы вашего пространства имен, которые им требуются. Пример 2.5 показывает несколько способов использования пространства имен.
Пример 2.5. Использование пространств имен
// Devices.h
#ifndef DEVICES_H__
#define DEVICES_H__
#include
#include
namespace hardware {
class Device {
public:
Device(): uptime_(0), status_("unknown") {}
unsigned long getUptime() const;
std::string getStatus() const;
void reset();
private:
unsigned long uptime_;
std::string status_;
};
class DeviceMgr {
public:
void getDeviceIds(std::list& ids) const;
Device getDevice(const std::string& id) const;
// Other stuff...
};
}
#endif // DEVICES_H__
// Devices.cpp
#include "Devices.h"
#include
#include
namespace hardware {
using std::string;
using std::list;
unsigned long Device::getUptime() const {
return(uptime__);
}
string Device::getStatus() const {
return(status_);
}
void DeviceMgr::getDeviceIds(list& ids) const {}
Device DeviceMgr::getDevice(const string& id) const {
Device d;
return(d);
}
}
// DeviceWidget.h
#ifndef DEVICEWIDGET_H__ #define DEVICEWIDGET_H__
#include "Devices.h"
namespace ui {
class Widget {/*... */ };
class DeviceWidget : public Widget {
public:
DeviceWidget(const hardware::Device& dev) : device_(dev) {}
// Some other stuff
protected:
hardware::Device device_;
};
}
#endif // DEVICEWIDGET_H__
// main.cpp
#include
#include "DeviceWidget.h"
#include "Devices.h"
int main( ) {
hardware::Device d;
ui::DeviceWidget myWidget(d);
// ...
}
Обсуждение
Пример 2.5 более сложен, но давайте разберем его по частям, так как он иллюстрирует несколько ключевых моментов, связанных с пространствами имен. Представьте, что вы пишете управляющее приложение, которое должно взаимодействовать с различным оборудованием. С целью устранения конфликтов имен вы можете разбить это приложение на два или более пространств имен или просто разделить его логически на две части.
Читать дальше