Безопасно ли использовать STL (TR1) shared_ptr между модулями (exes и dll) - PullRequest
16 голосов
/ 05 декабря 2008

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

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

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

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

Ответы [ 5 ]

14 голосов
/ 06 декабря 2008

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

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

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

12 голосов
/ 29 апреля 2011

Вы начинаете видеть, как невероятно 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_ptr s, даже не нуждаются в виртуальном деструкторе, потому что средство удаления всегда будет вызывать правильный деструктор, даже когда последний shared_ptr выйдет из области видимости. один экземпляр для базового класса.

7 голосов
/ 06 декабря 2008

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

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

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

2 голосов
/ 05 декабря 2008

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

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

2 голосов
/ 05 декабря 2008

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

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

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

...