Шаблоны C ++ и распределение заголовков - PullRequest
5 голосов
/ 11 мая 2011

Недавно я столкнулся с проблемами с выделением памяти в одной DLL (или * .so - переносимым кодом) и освобождением в другой DLL.До сих пор встречались следующие ошибки:

  • Это просто не работает - не удается выполнить assert () при отладке.
  • Это не работает, если одна DLL статически связана сстандартная библиотека C и другая DLL, динамически связанная с ней.
  • Не работает, если одна DLL выполняет выделение, тогда DLL выгружается, а другая DLL пытается освободить эту память.

По сути, я решил придерживаться правила, которое заключается в том, чтобы не делать выделения в одной DLL и освобождать ее в другой (и желательно хранить в одном файле cpp).Обычно это также означает, что я не должен делать выделения в заголовочном файле, который может совместно использоваться более чем одной DLL.Это означает Я не должен делать выделения в tempaltes (так как они все в заголовке), и это довольно большое ограничение.

Когда мне нужно создать новый объект в шаблонечто я сейчас делаю, так это выделяю память для этого файла cpp и только потом запускаю его c'tor с оператором размещения new.

// header
class MyBase
{
public:
  static void* allocate(std::size_t i_size);
};

template <typename T>
class MyClass: MyBase
{
public:
  T* createT();
};

temlpate <typename T>
T* MyClass<T>::createT()
{
  void* pMem = MyBase::allocate( sizeof(T) );
  return new (pMem) T;
}

// Cpp file
void* MyBase::allocate(std::size_t i_size)
{
  return malloc( i_size );
}

Хотя это работает, это немного уродливо.Это означает написание кода шаблона без использования new.

Еще одним следствием является то, что если вы не знаете, что шаблон был написан с использованием этого метода, вы должны использовать только методы const в заголовочном файле (включая другие шаблоны)(это предполагает, что методы const не выделяют и не освобождают память).Это включает в себя STL.Фактически, одно из мест, с которыми я столкнулся, было в векторе, размер которого был изменен одной динамической библиотекой (в HP-UX), а затем выгружен по сравнению с тем, как его d'tor был вызван другой динамической библиотекой.

Есть ли некоторыешироко известное решение для этого, что я просто пропускаю или это просто упущенная проблема?

Ответы [ 2 ]

4 голосов
/ 11 мая 2011

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

Нет, один не подразумевает другой.

Если ваши функции выделения и перераспределения являются шаблонами в заголовке, это все еще хорошо;просто убедитесь, что вы ограничиваете использование этих функций для любого данного объекта одним TU 1 .

Инкапсулируйте ваши объекты таким образом, чтобы он был недопустимым / запрещенным / неопределенным для кода в DLL 1 довызовите эти функции для объектов из DLL 2. Сделайте это для пользователя контрактом, напишите в комментариях, что право собственности на объекты остается в исходном контексте выделения, затем перейдите на к следующей части вашего проекта безснова беспокоиться об этом.

То, что функции доступны для всех TU, не имеет значения;в конце концов, вы можете всегда пытаться delete на этих вещах!


1 - Единица перевода .Примерно эквивалентно одному предварительно обработанному файлу .cpp.

0 голосов
/ 11 мая 2011

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

...