Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Как обеспечить надлежащее пересечение границ динамической библиотеки, применяя пользовательские средства удаления смарт-указателей

Anna | 25.06.2014 | нет комментариев

Многие специалисты С агитируют применять умственные указатели, утверждая, что из современного С , очевидное применение new должно вообще исчезнуть (ну, по крайней мере, когда в С 14 пофиксят неимениеstd::make_unique). Все динамические выделения памяти обязаны быть инкапсулированы либо в стандартную библиотеку, либо контейнеры типа std::vector, либо умственные указатели.

Смарт-указатели стандартной библиотеки могут быть настроены так, Дабы они сами занимались освобождением занимаемой ими памяти. Эта вероятность и заложена в основу результата на вопрос, поставленного в заголовке статьи.

Объект является пересекающим рубеж динамической библиотеки, если он инициализируется в одном блоке, а применяется в ином. Это происходит, когда, скажем, в dll инициализируется объект и возвращается указатель на него.

Представим, одна библиотека (либо исполнимый модуль) связывается с иной библиотекой, применяя фабрику для динамической инициализации объекта и приобретения указателя на него. Блок, тот, что использует данный указатель, может удалить указатель для освобождения области памяти, на которую он указывает. Если библиотека, которая выделяет память и блок, работающий с указателем, применяют разные версии динамического выделения памяти ОС (CRT в Windows), то возникнет оплошность. Пример этой задачи (в случае с Windows):
 
Как правило (до возникновения С 11), разработчики библиотеки обязаны были разрабатывать функции освобождения памяти для объектов, которые были выделены в пределах этой библиотеки, для того, Дабы избежать этой задачи. Это имело второстепенный результат: интерфейсы таких библиотек становились больше «тяжелыми», к тому же от них сейчас требовалось «ноу-хау» для правильного выделения и освобождения памяти для объектов библиотеки. В идеале, пользователя не должна была волновать сама схема выделения/освобождения, он легко должен был вызывать механизм библиотеки (скажем, фабрику) для выделения памяти, не заботясь об ее дальнейшем освобождении.

Переходим к коддингу

У нас будет два плана: 1-й будет состоять легко из файла main, использующий фабрику библиотеки для инициализации объектов из нее, 2-й будет иллюстрировать проблемную обстановку и ее решение.
Проблемная область — синглтон фабрика (ProblematicFactory), которая инициализирует объект и возвращает указатель на него. Решение — иной сингтон, тот, что, позже инициализации объекта, возвращает указательstd::unique_ptr, имеющий свое собственное средство удаления, изготавливающее освобождение памяти в DLL.
Если запустить программу в режиме отладки с определениемUSE_PROBLEMATIC_FACTORY_AND_CAUSE_HEAP_CORRUPTION, то дозволено увидеть, что отладчик обнаруживает повреждение «кучи».

Файл main
// main.cpp
#include <ProblematicFactory.h>
#include <SafeFactory.h>

// измените undef на define, Дабы увидеть assert'ы о повреждении кучи
#undef USE_PROBLEMATIC_FACTORY_AND_CAUSE_HEAP_CORRUPTION

int main()
{
#ifdef USE_PROBLEMATIC_FACTORY_AND_CAUSE_HEAP_CORRUPTION
  {
    // это выделение делается в DLL
    auto wMyObject = ProblematicFactory::getInstance().create();
    // это освобождение происходит в нынешнем блоке
    delete wMyObject;
    // если DLL и данный блок будут линковаться с одной и той же CLR DLL, 
    // удаление произойдет типично, напротив - это вызовет повреждение кучи
  }
#endif
  {
    auto wMyObject = SafeFactory::getInstance().create();
    // когда программа перейдет в дальнейший блок, wMyObject будет
    // механически удален, применяя пользовательское средство удаления,
    // реализованное в MyClass.h (см. дальше), функция освобождения
    // в библиотеке вызываться не будет
  } 
  {
    std::shared_ptr< MyClass > wMyObject = SafeFactory::getInstance().create();
  }
  return 0;
}
Проблемная фабрика

Это — нормальная реализация фабрики, которая возвращает указатель на объект, тот, что может быть сделан библиотекой.

// ProblematicFactory.h
#pragma once

#include "DllSwitch.h"
#include "MyClass.h"

class LIBRARYFACTORY_API ProblematicFactory
{
public:
  static ProblematicFactory & getInstance()
  {
     static ProblematicFactory wProblematicFactory;
     return wProblematicFactory;
  }
  MyClass * create() const
  {
     return new MyClass;
  }
private:
  ProblematicFactory() {};
}; 
Неопасная фабрика

Синтаксически, применение этой фабрики верно такое же, как и проблемной (см. main), но тут указатель инкапсулируется в std::unique_ptr, а не std::shared_ptr.

// SaveFactory.h
#pragma once

#include "DllSwitch.h"
#include "MyClass.h"

#include <memory>

class LIBRARYFACTORY_API SafeFactory
{
public:
  static SafeFactory & getInstance();
  // Тут std::unique_ptr не будет пересекать рубеж библиотеки, 
  // т.к. эта функция не будет входить в DLL. Создание произойдет
  // на стороне заказчика, следственно применяется std::unique_ptr заказчика.
  // Так же, нет необходимости задавать пользовательские средства
  // удаления, от того что для класса MyClass существует std::default_delete
  inline std::unique_ptr< MyClass > create() const
  {
    return std::unique_ptr< MyClass >(doCreate());
  }
private:
  SafeFactory();
  MyClass * doCreate() const;
};
Пересечение границы
// MyClass.h
#pragma once

#include "DllSwitch.h"

#include <memory>

class LIBRARYFACTORY_API MyClass
{
};

namespace std
{
template<>
class LIBRARYFACTORY_API default_delete< MyClass >
{
public:
  void operator()(MyClass *iToDelete)
  {
    delete iToDelete;
  }
};
}

Во всех вышеприведенных файлах подключается заголовочный файл DllSwitch.h, определяющаяLIBRARYFACTORY_API, его оглавление:

// DllSwitch.h
#pragma once

#ifdef LIBRARYFACTORY_EXPORTS
#define LIBRARYFACTORY_API __declspec(dllexport)
#else
#define LIBRARYFACTORY_API __declspec(dllimport)
#endif

UPD Все реализации функций нужно переносить в отдельные файлы

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB