Подтвердить что ты не робот

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

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

Однако безопасно ли использовать VС++ 2008 std:: tr1:: shared_ptr через модули?

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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