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

Смарт-поинтеры и RAII на службе программиста

Anna | 24.06.2014 | нет комментариев
Исторически так сложилось, что начальство желает, Дабы задача была исполнена стремительно. Для этого программисты сберегают красоту и чистоту кода. Данный пост возник как напоминание о редкоиспользуемых нововведениях в C 11 – смарт-поинтерах, дозволяющих указывать функтор для освобождения источников.
Для примера возьмем файловый поток FILE из stdio.h, тот, что любят за простоту и скорость, испробуем добавить ему красоту и базовую гарантию при исключениях:

unique_ptr<FILE, decltype(&fclose)> my_file(fopen("test.txt", "w"), &fclose);
if(my_file)
  fwrite("test", 4, 1, my_file.get());

В итоге код зависит только STL и требует маленький модификации обращений к файлу, пишется стремительно, выглядит современно. Вот так получился RAII в чистом виде.

Как это работает?

Функция fopen возвращает указатель на объект типа FILE, тот, что сохраняется в переменной my_file совместно с указателем на функцию fclose. Таким методом владение данным файловым потоком передается локальной переменной.

Когда функция fclose будет вызвана механически?

  1. При выходе из области видимости переменной (скажем, из функции).
  2. При появлении исключения позже создания my_file.
  3. При вызове функции присваивания объекту my_file.
  4. При вызове my_file.reset().

Какие убыточные расходы?

  1. Программисту требуется усложнить создание файла, удалить вызов fclose и дополнить вызовом unique_ptr<…>::get() все обращения к файлу.
  2. Компилятору в худшем случае понадобится ячейка памяти для хранения указателя на функцию удаления файла. В лучшем случае, он легко поставит вызов fclose в необходимом месте за вас, всецело оптимизировав объект my_file.

Какие плюсы у данного подхода?

  1. Как и с любым смарт-поинтером, вы очевидно указываете метод владения объектом. В данном случае, указано, что объект не является всеобщим (unique_ptr).
  2. Дозволено избавиться от лишнего copy-paste объявив свой тип так:
    typedef unique_ptr<FILE, decltype(&fclose)> MyFileType;
  3. Если применяется много файлов, есть толк написать небольшую обертку
    MyFileType MakeFile(const char* filename, const char* mode)
    {
      return unique_ptr<FILE, decltype(&fclose)>(fopen(filename, mode), &fclose);
    }
    

    … и пользоваться ей так:

    auto my_file = MakeFile("test.txt", "w");
  4. Разрешает избавиться от написания лишнего кода в деструкторе. Отчего лишнего? Вы теснее указали компилятору, как вы хотите руководить этим источником и сейчас это его работа.
  5. Дозволено применять объекты типа MyFileType в стандартных контейнерах STL:
    vector<MyFileType> my_files;
    my_files.push_back(MakeFile("test.txt", "w"));
    

    … и не тратить своё время на контроль времени жизни объектов. В C 11 vector<MyFileType> дозволено отважно возвращать из функции.

Вот еще несколько идей из C Runtime Library:

Те, кто озадачен либо увлекается оптимизацией под Windows знает, что доступ к выровненным данным происходит стремительней. Так дозволено сделать указатель на память, выровненную на 16 байт применяя библиотеку Microsoft Visual C Runtime:

unique_ptr<char[], decltype(&::_aligned_free)> my_buffer((char*)(_aligned_malloc(512, 16)), &_aligned_free);
my_buffer[0] = ‘x’; //  применение буфера

Написав один раз образец:

template<typename T>
unique_ptr<T[], decltype(&::_aligned_free)>
MakeAlignedBuffer(size_t element_count, size_t alignment = alignment_of<T>::value)
{
	return unique_ptr<T[], decltype(&::_aligned_free)>
		(reinterpret_cast<T*>(_aligned_malloc(element_count*sizeof(T), alignment)), &_aligned_free);
}

дозволено позабыть об ошибках выделения и удаления памяти различными функциями (сотворили через new[] в одном модуле, удалили через delete в ином).

А что делать, если определенным WinAPI источником обладает несколько объектов?

Для примера разглядим обстановку, когда в GUI приложении несколько различных объектов применяют функции, которые находятся в динамически-загружаемой DLL. В таком случае, не так легко запрограммировать своевременную выгрузку библиотеки как хотелось бы:
Загружаем библиотеку…

auto my_module = shared_ptr<HMODULE>(new HMODULE(LoadLibrary(_T("my_library.dll"))), [](HMODULE* instance){
	FreeLibrary(*instance);  //  выгружаем библиотеку когда ссылок на нее огромнее нет
});

Дальше раздаем my_module объектам…

module_owner1.set_module(my_module);
module_owner2.set_module(my_module);  //  либо можем хоть в vector их сложить

В объекте используем надобные функции…

if(my_module && *my_module)
{
	auto func1 = GetProcAddress(*my_module, "MyFunc");
}

Когда функциями перестаем пользоваться и счетчик ссылок на объект станет равен нулю – объект my_module будет вызвана функция FreeLibrary и объект будет удален.

Как применять лямбда-функцию в unique_ptr?

Нужно воспользоваться образцом function вот так:

auto my_instance = std::unique_ptr<HMODULE, function<void(HMODULE*)>>
                    (new HMODULE(LoadLibrary(_T("my_library.dll"))), [](HMODULE* instance){ FreeLibrary(*instance); });

Завершение

Уважаемые читатели, помните, что любая спецтехнология разрабатывается с определенной целью и не должна применяться там, где её применение не оправдано, т.е. не стоит кидаться заменять все указатели на смарт-поинтеры не задумываясь о необходимости и не анализируя итоги. Минусы у этих подходов тоже есть и они многократно обсуждались на прогре. Будьте профессиональны.
Спасибо.

 

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

 

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