Хотя это, кажется, очень распространенная проблема, я не получил много информации: Как я могу создать безопасный интерфейс между границами DLL относительно выделения памяти?
Это довольно известно это
// in DLL a
DLLEXPORT MyObject* getObject() { return new MyObject(); }
// in DLL b
MyObject *o = getObject();
delete o;
мог бы, конечно, привести к катастрофическим отказам. Но так как взаимодействия как то выше - как, осмелюсь сказать, - весьма распространены, должен быть способ гарантировать безопасное выделение памяти.
Конечно, можно было обеспечить
// in DLL a
DLLEXPORT void deleteObject(MyObject* o) { delete o; }
но возможно существуют лучшие пути (например, smart_ptr?). Я читал об использовании пользовательских средств выделения при контакте с контейнерами STL также.
Таким образом, мой запрос больше об общих указателях на статьи и/или литературу, имеющую дело с этой темой. Там специальные ошибки должны высматривать (обработка исключений?) и эта проблема ограничена только DLLs, или общие объекты UNIX "причинены" также?
Как вы предложили, для решения этой проблемы можно использовать boost::shared_ptr. В конструкторе вы можете передать пользовательскую функцию очистки, которая может быть методом deleteObject-метода dll, создавшей указатель. Пример:
boost::shared_ptr< MyObject > Instance( getObject( ), deleteObject );
Если вам не нужен C-интерфейс для вашей dll, вы можете заставить getObject
возвращать shared_ptr.
Перегрузка оператор new
, оператор delete
et. al для всех ваших классов DLL и реализовать их в DLL:
void* MyClass::operator new(size_t numb) {
return ::operator new(num_bytes);
}
void MyClass::operator delete(void* p) {
::operator delete(p);
}
...
Его можно легко поместить в общий базовый класс для всех классов, экспортируемых DLL.
Таким образом, выделение и освобождение полностью выполняется в куче DLL. Честно говоря, я не уверен, есть ли у него какие-либо серьезные подводные камни или проблемы с переносимостью, но у меня это работает.
Вы можете заявить, что это «определенно может привести к сбоям». Забавно - «могучий» означает полную противоположность «безусловно».
В любом случае, это утверждение в основном историческое. Существует очень простое решение: используйте 1 компилятор, 1 настройку компилятора и ссылку на DLL-форму CRT. (И вы, вероятно, можете уйти, пропуская последнее)
Нет конкретных статей, на которые можно было бы ссылаться, так как в настоящее время это не проблема. В любом случае вам понадобится 1 компилятор, 1 правило настройки. От него зависят такие простые вещи, как sizeof (std :: string)
, в противном случае у вас были бы массовые нарушения ODR.
Другой вариант, который может быть применим в некоторых обстоятельствах, - сохранить все выделение и освобождение внутри библиотеки DLL и предотвратить пересечение объектом этой границы. Вы можете сделать это, предоставив дескриптор, чтобы создание MyObject
создавало его внутри кода DLL и возвращало простой дескриптор (например, unsigned int
), через который выполняются все операции клиента. выполнено:
// Client code
ObjHandle h=dllPtr->CreateObject();
dllPtr->DoOperation(h);
dllPtr->DestroyObject(h);
Поскольку все распределение происходит внутри dll, вы можете убедиться, что она очищена, заключив в shared_ptr. Это в значительной степени метод, предложенный Джоном Лакосом в крупномасштабном C ++.
В «многоуровневой» архитектуре (очень распространенный сценарий) самый глубокий компонент отвечает за предоставление политики по вопросу (может возвращать shared_ptr <>
, как предложено выше, или «вызывающий отвечает за удаление this "или" никогда не удаляйте это, но вызывайте releaseFooObject ()
, когда закончите, и не обращайтесь к нему впоследствии »или ...) и компонент, находящийся ближе к пользователю, отвечает за соблюдение этой политики.
Двунаправленный информационный поток затрудняет определение обязанностей.
Эта проблема ограничена только библиотеками DLL, или же общие объекты UNIX тоже «наносятся»?
На самом деле, это еще хуже: вы можете столкнуться с этой проблемой так же легко со статически связанными библиотеками. Именно наличие границ кода внутри единого контекста выполнения дает возможность неправильно использовать или неправильно сообщать о каком-либо средстве.