2011-01-24 3 views
0

У меня возникла проблема с библиотекой C++, которую я пытаюсь написать. Это обычная настройка, один файл cpp, один заголовочный файл. Я хочу, чтобы заголовочный файл отображал только те части, которые предназначены для использования (например, у меня есть абстрактный базовый класс, я не хочу в файле заголовка). Пока что я просто работаю с одним файлом (я предполагаю, что это не имеет никакого значения, так как это включает препроцессор, который ни о чем не заботится).Декларации и пространства классов C++

Вы заметите, что «файл заголовка» распространяется на два места до и после файла реализации заголовка.

#include <stdio.h> 

// lib.h 
namespace foo { 
    template <class T> class A; 
} 

// lib.cpp 
namespace foo { 
    template <class T> class A { 
     private: 
     T i; 
     public: 
     A(T i) { 
      this->i = i; 
     } 

     T returnT() { 
      return i; 
     } 
    }; 
}; 

// lib.h 
namespace foo { 
    template <class T> T A<T>::returnT(); 
} 

// foo.cpp 
void main() { 
    foo::A<int> a = foo::A<int>(42); 
    printf("a = %d",a.returnT()); 
} 

Поэтому, естественно, я хочу, чтобы мой файл заголовка содержит только

namespace foo { 
    template <class T> class A; 
    template <class T> T A<T>::returnT(); 
} 

Но мой компилятор не нравится (он жалуется, что returnT не является членом foo::A<T>. Причину я не хочу помещать объявление класса в заголовок, так это то, что (как я его понимаю) будет содержать все личные и подобные вещи, которые я хотел бы скрыть.

Возможно, это только я, но следующий заголовочный файл кажется «плохим», по крайней мере, как «спецификация интерфейса». Он предоставляет некоторые из внутренних компонентов A, о которых пользователю библиотеки не нужно было бы знать.

// lib.h 
namespace foo { 
    template <class T> class A { 
     private: 
     int i; 
     public: 
     A(T); 
     T returnT(); 
    }; 
} 

// lib.cpp 
namespace foo { 
    template <class T> A<T>::A(T i) { 
     this->i = i; 
    } 
    template <class T> T A<T>::returnT() { 
     return i; 
    } 
}; 

Это приемлемый способ сделать это? Я бы хотел получить более абстрактный заголовочный файл, если это вообще возможно.

+0

ли весь первый блок кода код заголовка файла? Я запутался, потому что есть два блока с надписью «lib.h», но тогда у вас есть функция «main» ... –

+0

Привыкайте к раскрытию сведений о своей реализации при написании слова «шаблон». Вот как это работает, период. Вы можете сделать «файлы заголовков реализации», которые вы «включаете» в свой основной заголовок, но вы должны отправить исходный код. –

+0

@ Джеймс Макнеллис: первый фрагмент кода (серый блок) - это всего лишь тестовый файл, который я использовал для хранения всего в одном файле. Идея состоит в том, что это будет разделено на три файла: lib.h', 'lib.cpp' и' main.cpp', последний из которых просто является «пользовательским» тестом, вызывая в 'lib'. Проблема только в том, что для получения чего-то похожего на то, что я хочу, мне нужно разбить заголовок на одну часть над кодом реализации и на одну часть ниже. Имеет ли это смысл? – Svend

ответ

2

Есть две проблемы с .cpp файлов вы имеете дело здесь:

I. Если вы хотите поместить экземпляр этого класса в стеке (как вы делаете в основном()) потребности компилятора знать размер класса (выделить достаточное количество памяти). Для этого он должен знать членов и тем самым полную декларацию.

Единственный способ скрыть компоновку класса - это создать интерфейс и фабричный метод/функцию и поместить экземпляр в кучу на заводе.

В качестве примера (без шаблона, смотрите ниже, чтобы узнать, почему):

namespace foo { 
    class IA { 
    public: 
     virtual ~IA(); 
     virtual int returnT() = 0; 

     static IA *Create(); 
    }; 
} 

в вашем.CPP вы затем сделать:

namespace foo { 
    class A : public IA { 
    private: 
     int i; 
    public: 
     A() : 
     i(0) { 
     } 
     virtual ~A() { 
     } 
     virtual int returnT() { 
     return i; 
     } 
    }; 
    IA::~IA() { 
    } 

    IA *IA::Create() { 
    return new A(); 
    } 
} 

КСТАТИ: Использование смарт-указатели будут предложено ...

II. Поскольку вы используете шаблон, определения методов должны быть либо видимыми через файл заголовка, либо явно создаваться для определенного набора типов.

Таким образом, вы можете разделить ваш код в lib.h и lib_impl.h:

lib.h:

namespace foo { 
    template <typename T> class IA { 
    public: 
     virtual ~IA() { 
     } 
     virtual T returnT() = 0; 

     static IA *Create(); 
    }; 
} 

lib_impl.h:

namespace foo { 
    template <typename T> class A : public IA<T> { 
    private: 
     T i; 
    public: 
     A() : 
     i(T()) { 
     } 
     virtual ~A() { 
     } 
     virtual T returnT() { 
     return i; 
     } 
    }; 

    template <typename T> IA<T> *IA<T>::Create() { 
    return new A<T>(); 
    } 
} 

так что вы включите lib_impl.h, где вам понадобятся функции. Чтобы использовать явные инстанциацию добавить lib.cpp и пусть этот файл позволяет включить lib_impl.h:

lib.cpp:

#include <lib_impl.h> 
namespace foo { 
    template class IA<int>; 
    template class A<int>; 
    template class IA<float>; 
    template class A<float>; 
    template class IA<char>; 
    template class A<char>; 
    // ... 
} 
+0

Чтобы добавить к этому отличный ответ, вы также можете #include lib_impl.h в конце lib.h. Таким образом, с точки зрения пользователя, быстрый просмотр в lib.h покажет основные (интерфейс), не требуя просмотра деталей в lib_impl.h, и не нужно делать какую-либо специальную работу, чтобы выяснить, когда lib_impl .h должен быть включен или нет. –

+0

Этот подход (также описанный в FAQ) является тем, с чем я столкнулся. Умные указатели не совсем там, где я есть;) – Svend

+0

У меня вопрос. Насколько я понимаю, lib.cpp используется как своего рода «намек» на компилятор, что касается шаблонов для генерации. В FAQ также упоминается этот подход (пункт 35.13), но что конкретно делает этот синтаксис? Например, где я могу посмотреть в книге Страуструпов и узнать что-то? – Svend

5

Вы не можете отделить определение шаблона от его объявления. Они оба должны войти в файл заголовка вместе.

Для "почему?" Рекомендую прочитать "Why can't I separate the definition of my templates class from its declaration and put it inside a .cpp file?".


Возможно, у меня был неверный вопрос. Для того, чтобы решить, что также может быть ваш вопрос, это не действует:

namespace foo { 
    template <class T> class A;  
    template <class T> T A<T>::returnT(); 
} 

Он не действует по той же причине, что это не действует:

namespace foo { 
    class A; 
    int A::returnT(); 
} 

функции-члены должны быть объявлены внутри определения класса.

+0

ЧАСТО ЗАДАВАЕТСЯ Чрезвычайно ясно, в чем проблема. Благодаря! – Svend

+0

@ Свенд: Конечно. Я рад, что смог помочь. –

Смежные вопросы