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

Неправильно ли распределять память в DLL и указывать на нее клиентское приложение?

Я использую exe, который динамически загружает DLL. Функция в DLL выделяет память в куче и передает указатель на эту память в exe.

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

EDIT: В моем случае я планировал выделить и освободить память внутри самой DLL.

4b9b3361

Ответ 1

Вот некоторые причины, по которым у вызывающего есть указатель:

  • Симметричная семантика собственности. Это уже объяснено несколькими другими ответами.
  • Предотвращает несовпадение распределителя и деллалокатора. Как упоминалось в Ответ Esthete, если DLL выделяет указатель и возвращает его, вызывающий должен вызовите соответствующий освободитель, чтобы освободить его. Это не обязательно тривиально: DLL может быть статически связана с одной версией, скажем, malloc/free, а .exe связана с другой версией malloc/free. (Например, DLL может использовать версии выпуска, в то время как .exe использует специализированные версии отладки.)
  • Гибкость.. Если DLL предназначена для общего использования, если вызывающий абонент выделяет память, у нее появляется больше опций. Предположим, что вызывающий абонент не хочет использовать malloc и вместо этого хочет, чтобы память была выделена из определенного пула памяти. Возможно, это случай, когда вызывающий может предоставить указатель на память, выделенную в стеке. Если DLL выделяет сама память, вызывающий абонент не имеет каких-либо из этих параметров.

(Второй и третий пункты также могут быть устранены, если .exe снабжает распределитель/деллалокатор для использования DLL-кода.)

Ответ 2

Одной из основных идей шаблонов проектирования является владение. Идея - one who creates a resource (and thereby holds it in the pointer) should be responsible for deleting the resource. Это обеспечит неприкосновенность дизайна и более длительный срок службы проектов, его разработчик сможет увидеть меньше ошибок.

Итак, теперь в вашем случае DLL может быть прикреплена любым исполняемым файлом, и он может попытаться удалить ресурс, что может вызвать будущую проблему. Поэтому я думаю, что это было предложено для наоборот, и я бы назвал это хорошим советом.

Ответ 3

Я видел эту проблему раньше, и это связано с тем, что DLL и exe связаны иначе с CRT (статический, динамический MT и т.д.).

Я собираюсь передать указатель на память между DLL и исполняемым файлом, они должны предоставить некоторую функцию Free() для освобождения памяти из их соответствующих куч.

Ответ 4

Как правило, куча (The One Heap) принадлежит процессу, и не имеет значения, где вы выделяете, так что это будет работать нормально, , за исключением случаев, когда это не.

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

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

Что делает ситуацию с бесполезной кучей настолько порочной, что вы обычно не знаете, что произойдет, или когда (или если кто-то заметит).

Вы можете получить возвращаемое значение NULL из функции кучи или исключения. Ваше приложение может быть подготовлено для любого из них, или это может быть не так (честно говоря, вы всегда проверяете возвращаемые значения, не так ли?). Это может привести к сбою сразу после освобождения, или вы можете просто без проблем протекать память и выходить из адресного пространства (или памяти) минут или часов позже, и никто не узнает, почему. Или, что-то еще.
Таким образом, то, что вы видите, может вообще не быть в (видимой) корреляции с тем, что вызвало проблему.

Ответ 5

Я хочу только отметить, что передача распределителя в DLL для выделения памяти совершенно безопасна и является стандартной моделью, используемой самой библиотекой С++ std. В этом случае распределение осуществляется в DLL с помощью метода передается от вызывающего абонента, избегая передавать указатели, и избежать проблемы увязки с различными реализациями malloc().

Ответ 6

exe и dll могут иметь разные кучи. когда либо пытаются освободить память, выделенную другим, так и освободится, и есть утечка.

только если оба, exe и dll динамически используют CRT с той же версией CRT, они используют одну и ту же кучу.

поэтому очень полезно советовать делать распределение и бесплатно в том же двоичном файле.

Ответ 7

Как уже отмечал @kumar_m_kiran, это касается проблемы с владельцем, однако я хочу отметить, что @aleguna тоже верна, поэтому imho, правильное правило: "Либо выделяйте и освободите одну и ту же память в DLL или в EXE, но не в обоих".

Ответ 8

Я бы сказал, что в целом плохая практика заключается в том, чтобы раздавать исходные указатели. В случае DLL он должен вернуть смарт-указатель с пользовательским удалением, который надлежащим образом обрабатывает очистку (опции std::unique_ptr, std::shared_ptr или boost::shared_ptr в порядке предпочтения).

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

Ответ 9

Это не обязательно плохая практика, но это опасная практика (и, вероятно, плохая). Вам нужно внимательно подумать о том, кто несет ответственность за освобождение памяти. Exe, вероятно, не сможет (или не сможет) бесплатно освободить библиотеку DLL, поэтому предположительно будет передавать этот указатель обратно в DLL позже. Итак, теперь мы передаем указатель между EXE и DLL, что не очень приятно.

Ответ 10

Я бы сказал, нет, это не "плохая практика". По моему опыту, вам просто нужно быть осторожным, чтобы вы отпустили этот указатель в правильном пространстве памяти. Я создал графический движок, который распределял ресурсы в нескольких DLL (каждая DLL представляла мини-игру); использование shared_ptr (в то время, когда оно было Boost, я уверен, что С++ 11 (или более новый) std:: shared_ptr поддерживает ту же семантику). Я бы поставил функцию, которая удалит память в нужном месте. На данный момент основная проблема заключается в том, что вы гарантируете, что вы освободите свои shared_ptrs, прежде чем освобождать DLL. Я не могу вспомнить сейчас, но мы могли бы использовать список shared_ptrs, принадлежащих оболочке DLL/DLL, а все остальные применения указателя были через weak_ptr TO, что shared_ptr.