Память / управление "кучей" через DLLs

Хотя это, кажется, очень распространенная проблема, я не получил много информации: Как я могу создать безопасный интерфейс между границами 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 "причинены" также?

20
задан msi 15 February 2010 в 13:31
поделиться

6 ответов

Как вы предложили, для решения этой проблемы можно использовать boost::shared_ptr. В конструкторе вы можете передать пользовательскую функцию очистки, которая может быть методом deleteObject-метода dll, создавшей указатель. Пример:

boost::shared_ptr< MyObject > Instance( getObject( ), deleteObject );

Если вам не нужен C-интерфейс для вашей dll, вы можете заставить getObject возвращать shared_ptr.

13
ответ дан 30 November 2019 в 00:13
поделиться

Перегрузка оператор 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. Честно говоря, я не уверен, есть ли у него какие-либо серьезные подводные камни или проблемы с переносимостью, но у меня это работает.

9
ответ дан 30 November 2019 в 00:13
поделиться

Вы можете заявить, что это «определенно может привести к сбоям». Забавно - «могучий» означает полную противоположность «безусловно».

В любом случае, это утверждение в основном историческое. Существует очень простое решение: используйте 1 компилятор, 1 настройку компилятора и ссылку на DLL-форму CRT. (И вы, вероятно, можете уйти, пропуская последнее)

Нет конкретных статей, на которые можно было бы ссылаться, так как в настоящее время это не проблема. В любом случае вам понадобится 1 компилятор, 1 правило настройки. От него зависят такие простые вещи, как sizeof (std :: string) , в противном случае у вас были бы массовые нарушения ODR.

5
ответ дан 30 November 2019 в 00:13
поделиться

Другой вариант, который может быть применим в некоторых обстоятельствах, - сохранить все выделение и освобождение внутри библиотеки DLL и предотвратить пересечение объектом этой границы. Вы можете сделать это, предоставив дескриптор, чтобы создание MyObject создавало его внутри кода DLL и возвращало простой дескриптор (например, unsigned int ), через который выполняются все операции клиента. выполнено:

// Client code
ObjHandle h=dllPtr->CreateObject();
dllPtr->DoOperation(h);
dllPtr->DestroyObject(h);

Поскольку все распределение происходит внутри dll, вы можете убедиться, что она очищена, заключив в shared_ptr. Это в значительной степени метод, предложенный Джоном Лакосом в крупномасштабном C ++.

2
ответ дан 30 November 2019 в 00:13
поделиться

В «многоуровневой» архитектуре (очень распространенный сценарий) самый глубокий компонент отвечает за предоставление политики по вопросу (может возвращать shared_ptr <> , как предложено выше, или «вызывающий отвечает за удаление this "или" никогда не удаляйте это, но вызывайте releaseFooObject () , когда закончите, и не обращайтесь к нему впоследствии »или ...) и компонент, находящийся ближе к пользователю, отвечает за соблюдение этой политики.

Двунаправленный информационный поток затрудняет определение обязанностей.


Эта проблема ограничена только библиотеками DLL, или же общие объекты UNIX тоже «наносятся»?

На самом деле, это еще хуже: вы можете столкнуться с этой проблемой так же легко со статически связанными библиотеками. Именно наличие границ кода внутри единого контекста выполнения дает возможность неправильно использовать или неправильно сообщать о каком-либо средстве.

1
ответ дан 30 November 2019 в 00:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: