Скажем, с общим кодом, например следующим:
y.hpp:
#ifndef Y_HPP
#define Y_HPP
// LOTS OF FILES INCLUDED
template <class T>
class Y
{
public:
T z;
// LOTS OF STUFF HERE
};
#endif
Теперь мы хотим иметь возможность использовать Y в классе (скажем, X), который мы создаем. Однако мы не хотим, чтобы пользователи X должны были включать заголовки Y.
Итак, мы определяем класс X, что-то вроде этого:
x.hpp:
#ifndef X_HPP
#define X_HPP
template <class T>
class Y;
class X
{
public:
~X();
void some_method(int blah);
private:
Y<int>* y_;
};
#endif
Обратите внимание: поскольку y_ является указателем, нам не нужно включать его реализацию.
Реализация находится в x.cpp, который компилируется отдельно:
x.cpp:
#include "x.hpp"
#include "y.hpp"
X::~X() { delete y_; }
void X::someMethod(int blah) { y_->z = blah; }
Итак, теперь наши клиенты могут просто включить «x.hpp» для использования X, не включая и не обрабатывая все заголовки «y.hpp»:
main.cpp:
#include "x.hpp"
int main()
{
X x;
x.blah(42);
return 0;
}
И теперь мы можем компилировать main.cpp
и x.cpp
отдельно, и при компиляции main.cpp
мне не нужно включать y.hpp
.
Однако с этим кодом мне пришлось использовать необработанный указатель, и, кроме того, мне пришлось использовать удаление.
Итак, вот мои вопросы:
(1) Можно ли сделать Y прямым членом (а не указателем на Y) X без необходимости включать заголовки Y? (Я сильно подозреваю, что ответ на этот вопрос отрицательный)
(2) Можно ли использовать класс интеллектуальных указателей для обработки кучи, выделенной Y? unique_ptr
кажется очевидным выбором, но когда я меняю строку в x.hpp
от:
Y<int>* y_;
to:
std::unique_ptr< Y<int> > y_;
и включить и скомпилировать в режиме С++ 0x, я получаю сообщение об ошибке:
/usr/include/c++/4.4/bits/unique_ptr.h:64: error: invalid application of ‘sizeof’ to incomplete type ‘Y<int>’
/usr/include/c++/4.4/bits/unique_ptr.h:62: error: static assertion failed: "can't delete pointer to incomplete type"
так можно ли в любом случае сделать это, используя стандартный интеллектуальный указатель вместо необработанного указателя, а также необработанное удаление в пользовательском деструкторе?
Решение:
Говард Хиннант все сделал правильно, все, что нам нужно сделать, это изменить x.hpp
и x.cpp
следующим образом:
x.hpp:
#ifndef X_HPP
#define X_HPP
#include <memory>
template <class T>
class Y;
class X
{
public:
X(); // ADD CONSTRUCTOR FOR X();
~X();
void some_method(int blah);
private:
std::unique_ptr< Y<int> > y_;
};
#endif
x.cpp:
#include "x.hpp"
#include "y.hpp"
X::X() : y_(new Y<int>()) {} // ADD CONSTRUCTOR FOR X();
X::~X() {}
void X::someMethod(int blah) { y_->z = blah; }
И мы можем использовать unique_ptr. Спасибо, Говард!
Обоснование решения:
Люди могут поправить меня, если я ошибаюсь, но проблема с этим кодом заключалась в том, что неявный конструктор по умолчанию пытался инициализировать Y по умолчанию, и поскольку он ничего не знает о Y, он не может этого сделать. Явно говоря, что мы определим конструктор в другом месте, компилятор думает: «Ну, мне не нужно беспокоиться о создании Y, потому что он скомпилирован в другом месте».
Действительно, надо было в первую очередь добавить конструктор, без него моя программа глючит.