2008-12-05 6 views
16

Я знаю, что новое-что-то в одном модуле и удаление его в другом часто может вызывать проблемы в VC++. Проблемы с разными режимами работы. Смешивающие модули со статически связанными режимами работы и/или динамически связанными несоответствиями версиями оба могут закручивать содержимое, если я правильно помню.Можно ли использовать STL (TR1) shared_ptr между модулями (exes и dlls)

Тем не менее, можно ли использовать VC++ 2008 std :: tr1 :: shared_ptr через модули?

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

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

ответ

14

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

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

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

2

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

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

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

+0

Я управляю двумя задействованными модулями, поэтому я буду в порядке, даже если shared_ptr имеет эти ограничения. Но я не хочу этого делать, если это может быть плохо. – Aardvark 2008-12-05 21:35:33

+0

Я регулярно использую несколько модулей, где ресурсы выделены в одном из них и выпущены в другом. – dalle 2008-12-05 22:05:37

+0

Важной частью является * если модули используют точно такую ​​же библиотеку времени выполнения *. Даже смешение отладочных и выпускных сборок - верный билет в ад. – MP24 2009-10-12 18:49:27

2

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

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

7

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

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

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

10

Вы начинаете видеть, как невероятно amazing shared_ptr is :)

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

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

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

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

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