FILE*
, написанную на C++98:
class File { FILE* handle; public: File(const char* filename) { if ( !(handle = fopen(filename, "r")) ) throw std::runtime_error("blah blah blah"); } ~File() { if (handle) fclose(handle); } // ... private: File(const File&); //запретить копирование void operator=(const File&); //запретить присваивание };
Мы запретили копирование и присваивание объектов этого класса, поскольку копирование FILE*
потребовало бы некоторых платформо-зависимых ухищрений, и вообще не имеет особого физического смысла.
Что же делать, если требуется хранить целый список объектов типа File
? К сожалению, мы не можем использовать File
в стандартном контейнере, то есть такой код просто не скомпилируется:
std::vector<File> files; files.push_back(File("data.txt"));
Типичным решением такой проблемы в C++98 является использование shared_ptr
:
std::vector<boost::shared_ptr<File> > files; files.push_back(boost::shared_ptr<File>(new File("data.txt")) );
Такое решение не особо радует глаз, особенно учитывая то, что мы используем динамическую память там, где, казалось бы, она не нужна.
Если мы разрешаем использование C++11, то картина сильно меняется. С появлением move semantics, стандартные контейнеры больше не требуют наличия обычных конструктора копирования и оператора присваивания, если только вы не собираетесь копировать контейнер целиком. Вместо них достаточно наличия семантики перемещения. Давайте посмотрим, как мы можем переписать пример с классом File
на C++11:
class File { FILE* handle; public: File(const char* filename) { if ( !(handle = fopen(filename, "r")) ) throw std::runtime_error("blah blah blah"); } ~File() { if (handle) fclose(handle); } File(File&& that) { handle = that.handle; that.handle = nullptr; } File& operator=(File&& that) { std::swap(handle, that.handle); return *this; } File(const File&) = delete; //запретить копирование void operator=(const File&) = delete; //запретить присваивание // ... };
Мы снова запрещаем обычное копирование, но разрешаем перемещение объекта. Теперь такой код работает:
std::vector<File> files; files.push_back(File("data1.txt")); files.push_back(File("data2.txt")); files.erase(files.begin());
Кроме того, благодаря variadic templates, в контейнерах появилась новая шаблонная функция emplace_back
, которая позволяет создать объект прямо в контейнере, не копируя его:
std::vector<File> files; files.emplace_back("data1.txt"); // добавить File("data1.txt") в конец массива
Надеюсь, что данная заметка наглядно показывает, насколько важным нововведением является семантика перемещения объектов. Всем успехов в переходе на новый стандарт!
ссылка на оригинал статьи http://habrahabr.ru/post/174019/
Добавить комментарий