Обеспечение функциональности в форме классов через DLL само по себе хорошо. Однако вы должны быть осторожны, чтобы отделить интерфейс от реализации. Насколько осторожно зависит от того, как будет использоваться ваша DLL. Для игрушечных проектов или утилит, которые остаются внутренними, вам даже не нужно думать об этом. Для библиотек DLL, которые будут использоваться несколькими клиентами в рамках компилятора who-знает-какой, вы должны быть очень осторожны.
Рассмотрим:
class MyGizmo
{
public:
std::string get_name() const;
private:
std::string name_;
};
Если MyGizmo
будет использоваться третьими лицами, этот класс не вызовет у вас головной боли. Очевидно, что закрытые переменные-члены являются проблемой, но тип возвращаемого значения для get_name()
является такой же проблемой. Причина в том, что детали реализации std::string
являются частью его определения. Стандарт диктует минимальный набор функций для std::string
, но разработчики компиляторов могут свободно реализовать то, что они выбирают. Один может иметь функцию с именем realloc()
для обработки внутреннего перераспределения, в то время как другой может иметь функцию с именем buy_node()
или что-то в этом роде. То же самое верно для членов данных. Одна реализация может использовать 3 size_t
и char*
, а другая может использовать std::vector
. Дело в том, что ваш компилятор может подумать, что std::string
- это n байтов и имеет такие-то члены, тогда как другой компилятор (или даже другой уровень патча того же компилятора) может подумать, что он выглядит совершенно иначе.
Одним из решений этой проблемы является использование интерфейсов . В публичном заголовке вашей DLL вы объявляете абстрактный класс, представляющий полезные средства, предоставляемые вашей DLL, и средства для создания класса, такие как:
DLL.H:
class MyGizmo
{
public:
static MyGizmo* Create();
virtual void get_name(char* buffer_alloc_by_caller, size_t size_of_buffer) const = 0;
virtual ~MyGizmo();
private:
MyGizmo(); // nobody can create this except myself
};
... и затем во внутренних компонентах вашей DLL вы определяете класс, который фактически реализует MyGizmo
:
mygizmo.cpp:
class MyConcreteGizmo : public MyGizmo
{
public:
void get_name(char* buf, size_t sz) const { /*...*/ }
~MyGizmo() { /*...*/ }
private:
std::string name_;
};
MyGizmo* MyGizmo::Create()
{
return new MyConcreteGizmo;
}
Это может показаться болью, и это так. Если ваша DLL будет использоваться только внутренне только одним компилятором, возможно, нет причин идти на неприятности. Но если ваша DLL будет использоваться моими несколькими компиляторами для внутренних целей или для внешних клиентов, это избавит вас от головной боли в будущем.