Безопасно ли использовать STL (TR1)shared_ptr между модулями (exe-файлами и dll)

StackOverflow https://stackoverflow.com/questions/345003

Вопрос

Я знаю, что добавление чего-то нового в одном модуле и удаление этого в другом часто может вызвать проблемы в VС++.Проблемы с разными средами выполнения.Смешивание модулей со статически связанными средами выполнения и/или динамически связанными несоответствиями версий может испортить ситуацию, если я правильно помню.

Однако безопасно ли использовать std::tr1::shared_ptr из VC++ 2008 в разных модулях?

Поскольку существует только одна версия среды выполнения, которая даже знает, что такоеshared_ptr, статическое связывание — моя единственная опасность (на данный момент...).Я думал, что читал, что версию Shared_ptr от Boost можно безопасно использовать таким образом, но я использую версию Редмонда...

Я пытаюсь избежать специального вызова освобождения объектов в модуле выделения.(или что-то вроде «удалить это» в самом классе).Если все это кажется немного хаотичным, я использую это для модульного тестирования.Если вы когда-либо пытались выполнить модульное тестирование существующего кода C++, вы можете понять, как творческий вам нужно быть время от времени.Моя память выделяется EXE-файлом, но в конечном итоге будет освобождена в DLL (если подсчет ссылок работает так, как я думаю).

Это было полезно?

Решение

Освобождение памяти безопасно, если все это происходит из одного и того же контекста управления памятью . Вы определили наиболее распространенную проблему (различные среды выполнения C ++); наличие отдельных куч - еще одна менее распространенная проблема, с которой вы можете столкнуться.

Еще одна проблема, о которой вы не упомянули, но которая может усугубляться общими указателями, - это когда код объекта существует в DLL и создается DLL, но другой объект за пределами DLL заканчивается ссылкой на него. (через общий указатель). Если этот объект будет уничтожен после выгрузки DLL (например, если это статический объект уровня модуля или DLL явно выгружена с помощью FreeLibrary(), деструктор общего объекта потерпит крах.

Это может вас укусить, если вы попытаетесь написать слабосвязанные плагины на основе DLL. Это также причина того, что COM позволяет библиотекам DLL решать, когда их можно выгружать, а не позволяет COM-серверам запрашивать-выгружать их.

Другие советы

Вы начинаете понимать, насколько невероятно удивительно shared_ptr является :)

Безопасность за пределами границ DLL — это именно то, что нужно shared_ptr было задумано (помимо прочего, конечно).

Вопреки тому, что говорили другие, вам даже не нужно передавать собственный удалитель при создании shared_ptr, поскольку значение по умолчанию уже что-то вроде

template <typename T>
struct default_deleter {
    void operator()( T * t ) { delete t; }
};

и

shared_ptr<Foo> foo( new Bar );

эквивалентно

shared_ptr<Foo> foo( new Bar, default_deleter<Bar>() );

(т.е.не существует такой вещи, как shared_ptr без удаления).

Из-за стирания типа, выполняемого на средстве удаления, delete это называется воля всегда будь тот из DLL, который создан экземпляр тот shared_ptr, ни в коем случае не тот из DLL, где находится последний shared_ptr выходит за рамки (т.тот shared_ptr вызов средства удаления вызовет его через указатель на функцию, помещенную туда оригиналом shared_ptr).

Сравните это с auto_ptr, который встраивает delete оператор непосредственно в его (встроенном) деструкторе, что означает, что delete библиотеки DLL, которая разрушает тот auto_ptr используется, создавая те же проблемы, что и удаление голого указателя.

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

Если вы заинтересованы, используйте форму конструктора shared_ptr, которая принимает аргумент удаления. Удалитель может перезвонить в модуль, который выделил объект, так что удаление происходит в правильном контексте.

Документация Boost утверждает, что она на 100% совместима с TR1, так что, надеюсь, в этом нет ничего обманчивого:

http://www.boost.org /doc/libs/1_37_0/libs/smart_ptr/shared_ptr.htm#constructors

Я предполагаю, что это так же безопасно, как использовать любой из классов в std по модулям.

То есть:Будет безопасно, если модули будут использовать одну и ту же библиотеку времени выполнения и одни и те же параметры и параметры компилятора.

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

Лучший совет, который я видел по общей теме, заключается в том, что память должна быть освобождена в том же контексте, в котором она выделена. Это не исключает возможности передачи библиотекой указателя, который код приложения должен освободить, поэтому я бы сказал, что вы, вероятно, безопасно передаете shared_ptr таким образом, поскольку это та же самая общая ситуация.

Если семантика вашей системы означает, что указатель фактически передается (в смысле владения) из вашего exe в вашу dll, тогда auto_ptr может быть лучшим решением. Однако, если ваш указатель действительно является общим, то, вероятно, лучшим решением является shared_ptr.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top