|
Функция с произвольным числом аргументовSmart pointer Smart pointer — это объект, работать с которым можно как с обычным указателем, но при этом, в отличии от последнего, он предоставляет некоторый дополнительный функционал (например, автоматическое освобождение закрепленной за указателем области памяти). Умные указатели призваны для борьбы с утечками памяти, которые сложно избежать в больших проектах. Они особенно удобны в местах, где возникают исключения, так как при последних происходит процесс раскрутки стека и уничтожаются локальные объекты. В случае обычного указателя — уничтожится переменная-указатель, при этом ресурс останется не освобожденным. В случае умного указателя — вызовется деструктор, который и освободит выделенный ресурс. В новом стандарте появились следующие умные указатели: unique_ptr, shared_ptr и weak_ptr. Все они объявлены в заголовочном файле <memory>. Unique_ptr Объект этого класса теряет права владения ресурсом при копировании (присваивании, использовании в конструкторе копий, передаче в функцию по значению). В unique_ptr запрещает копирование: std::unique_ptr<int> x_ptr(new int(42));//int *x_ptr;std::unique_ptr<int> y_ptr; // ошибка при компиляцииy_ptr = x_ptr; // ошибка при компиляцииstd::unique_ptr<int> z_ptr(x_ptr);Изменение прав владения ресурсом осуществляется с помощью вспомогательной функции std::move (которая является частью механизма перемещения). std::unique_ptr<int> x_ptr(new int(42));std::unique_ptr<int> y_ptr; // права владения переходят// к y_ptr, а x_ptr начинает указывать на null pointery_ptr = std::move(x_ptr);unique_ptr обладает методами reset(), который сбрасывает права владения, и get(), который возвращает сырой (классический) указатель. std::unique_ptr<Foo> ptr = std::unique_ptr<Foo>(new Foo); // получаем классический указательFoo *foo = ptr.get();foo->bar(); // сбрасываем права владенияptr.reset();Shared_ptr shared_ptr реализует подсчет ссылок на ресурс. Ресурс освободится тогда, когда счетчик ссылок на него будет равен 0. Как видно, система реализует одно из основных правил сборщика мусора. std::shared_ptr<int> x_ptr(new int(42));std::shared_ptr<int> y_ptr(new int(13)); // после выполнения данной строчки, ресурс// на который указывал ранее y_ptr (int(13)) освободится,// а на int(42) будут ссылаться оба указателяy_ptr = x_ptr; std::cout << *x_ptr << "\t" << *y_ptr << std::endl; // int(42) освободится лишь при уничтожении последнего ссылающегося// на него указателяТакже как и unique_ptr, данный класс предоставляет методы get() и reset(). auto ptr = std::make_shared<Foo>(); Foo *foo = ptr.get();foo->bar(); ptr.reset();При работе с умным указателем, следует опасаться их создания на лету. Например, следующий код может привести к утечки памяти. someFunction(std::shared_ptr<Foo>(new Foo), getRandomKey());Почему? Да потому, что стандарт C++ не определяет порядок вычисления аргументов. Может случиться так, что сначала выполнится new Foo, затем getRandomKey() и лишь затем конструктор shared_ptr. Если же функция getRandomKey() бросит исключение, до деструктора shared_ptr дело не дойдет, хотя ресурс (объект Foo) был уже выделен. В случае с shared_ptr есть выход — использовать фабричную функцию std::make_shared<>, которая создает объект заданного типа и возвращает shared_ptr указывающий на него. someFunction(std::make_shared<Foo>(), getRandomKey());Почему и как это работает? Очень просто. Как я уже сказал выше, make_shared возвращает shared_ptr. Этот результат является временным объектом, а стандарт C++ четко декларирует, что временные объекты уничтожаются, в случае появления исключения. Weak_ptr Данный класс позволяет разрушить циклическую зависимость, которая, несомненно, может образоваться при использовании shared_ptr. Предположим, есть следующая ситуация: class Bar; class Foo{public: Foo() { std::cout << "Foo()" << std::endl; } ~Foo() { std::cout << "~Foo()" << std::endl; } std::shared_ptr<Bar> bar;}; class Bar{public: Bar() { std::cout << "Bar()" << std::endl; } ~Bar() { std::cout << "~Bar()" << std::endl; } std::shared_ptr<Foo> foo;}; int main(){ auto foo = std::make_shared<Foo>(); foo->bar = std::make_shared<Bar>(); foo->bar->foo = foo; return 0;}Как видно, объект foo ссылается на bar и наоборот. Образован цикл, из-за которого не вызовутся деструкторы объектов. Для того чтобы разорвать этот цикл, достаточно в классе Bar заменить shared_ptr на weak_ptr. Почему образован цикл? Давайте разберемся. При выходе из блока (в данном случае функции main()) уничтожаются локальные объекты. Локальным объектом является foo. При уничтожении foo счетчик ссылок на его ресурс уменьшится на единицу. Однако, ресурс освобожден не будет, так как на него есть ссылка со стороны ресурса bar. А на bar есть ссылка со стороны того же ресурса foo. weak_ptr не позволяет работать с ресурсом напрямую, но зато обладает методом lock(), который генерирует shared_ptr(). std::shared_ptr<Foo> ptr = std::make_shared<Foo>();std::weak_ptr<Foo> w(ptr); if (std::shared_ptr<Foo> foo = w.lock()){ foo->doSomething();}
Rvalue-ссылки По стандарту C++ временный объект, появившийся в результате вычисления выражения, можно передавать в функции, но только по константной ссылке (const &). Функция не в состоянии определить, можно ли рассматривать переданный объект как временный и допускающий модификацию (константный объект, который тоже может быть передан по такой ссылке, нельзя модифицировать (легально)). Это не проблема для простейших структур наподобие complex, но для сложных типов, требующих выделения-освобождения памяти, уничтожение временного объекта и создание постоянного может отнимать много времени, в то время как можно было бы просто напрямую передать указатели. В C++11 появился новый тип ссылки — rvalue -ссылка (англ. rvalue reference). Его объявление следующее: type &&. Новые правила разрешения перегрузки позволяют использовать разные перегруженные функции для неконстантных временных объектов, обозначаемых посредством rvalues, и для всех остальных объектов. Данное нововведение позволяет реализовывать семантику переноса (Move semantics). Например, std::vector — это простая обёртка вокруг Си-массива и переменной, хранящей его размер. Конструктор копирования std::vector::vector(const vector &x) создаст новый массив и скопирует информацию; конструктор переноса std::vector::vector(vector &&x) может просто обменяться указателями и переменными, содержащими длину. Конструктор переноса из временного объекта: Buffer (Buffer &&);Перенос временного объекта (быстрый): Buffer & operator = (Buffer &&);Пример: std::string _name; size_t _size; std::unique_ptr<T[]> _buffer;
// move constructor Buffer(Buffer&& temp): _name(std::move(temp._name)), _size(temp._size), _buffer(std::move(temp._buffer)) { temp._buffer = nullptr; temp._size = 0; } // move assignment operator Buffer& operator=(Buffer&& temp) { assert(this!= &temp); // assert if this is not a temporary _buffer = nullptr; _size = temp._size; _buffer = std::move(temp._buffer); _name = std::move(temp._name); temp._buffer = nullptr; temp._size = 0; return *this; }};
Функция с произвольным числом аргументов Если заранее не известно число аргументов (например, функция printf), то указать то, что количество неизвестных произвольно можно следующим образом: int printf(const char *format,...); Такое написание означает, что функция имеет один обязательный параметр (строка) и неопределенное количество (>=0) необязательных. Кроме того что количество параметров у функции неограничено, эти параметры могут быть любого типа. Для этого необходимо подключить библиотеку stdarg.h, объявить переменную типа va_list (на самом деле это эквивалент char*) и воспользоваться следующими функциями и типами:
unsigned summ(unsigned num,...) { //Переменная типа va_list – список аргументов va_list args; unsigned sum = 0; unsigned testsum = 0; //Устанавливаем указатель на первый элемент va_start(args, num);
while (num--) { //Достаём следующий, указывая тип аргумента testsum += va_arg(args, unsigned); if (testsum >= sum) { sum = testsum; } else { exit(UNSIGNED_OVERFLOW); } }
va_end(args); }
Работа с файлами Создание и удаление файлов и каталогов Библиотека: <direct.h> Для того, чтобы создать каталог достаточно вызова функции mkdir. Ее преимущество в том, что если в нее передать дополнительный параметр, она доступна в UNIX-системах. Единственный ее параметр - путь к каталогу. int _mkdir(const char *path);при ошибке возвращает -1, а при успехе - 0. Для того, чтобы удалить созданный раннее каталог достаточно вызова rmdir. К каталогу, который удаляем, предъявляются три требования: - он должен буть пустым; - он не является текущей рабочей директорией - он не является корневым каталогом int _rmdir(const char *path);Для того, чтобы создать файл на диске, используйте функцию _creat, которая создает новый или обрезает существующий файл. int _creat(const char *path, int amode);int _wcreat(const wchar_t *path, int amode);Первый параметр - путь к создаваемогу файлу, второй - режим файла. Файл может быть открыт и для записи, и для чтения, и для обеих операций.
Эти константы храняться в библиотеке sys/stat.h Для того, чтобы настроить разрешения для каталога (аналогично режиму файла), используйте функцию chmod. int chmod(const char *path, int amode);int _wchmod(const wchar_t *path, int amode);Функция устанавливает режим amode для каталога path. Для того, чтобы узнать режим файла, используйте функцию access. int access(const char *filename, int amode);int _waccess(const wchar_t *filename, int amode);Определяет возможен ли доступ к файлу filename при режиме amode.
Функция access возвращает значение 0, если файл имеет заданный режим mode. Возвращаемое значение -1 свидетельствует о том, что названный файл не существует или недоступен в заданном amode. Существует два способа удалить файл: через функцию _unlink (io.h) или remove (stdio.h). int remove(const char *filename);Функция удаляет файл filename и возвращает нуль при успехе. Ее аналог - функция _unlink: int _unlink(const char *filename);int _wunlink(const wchar_t *filename);Замечание: если файл открыт, следует закрыть его перед удалением.
Функция int rename(const char * oldfilename, const char * newfilename) изменяет имя файла или каталога, указанного в oldfilename на новое имя, указанное в newfilename. · oldfilename -строка, содержащая имя файла, который будет переименован и (или) перемещен. Этот файл должен существовать и быть доступным. · newfilename -строка, содержащая новое имя файла. Она не должно быть именем существующего файла, если это так, то результат работы программы будет непредсказуем, а точнее, он будет зависеть от того, как среагирует используемая ОС, может быть или отказ или переопределение. Если файл успешно переименован, возвращается нулевое значение. Ошибка — числовое значение, представляющее тип сбоя. Строка, интерпретирующая это значение может быть выведена на стандартный поток ошибок при вызове функции perror.
Копирование файла:
Функция int copyFile(const char * newfilename, const char * oldfilename) копирует файла, указанный в oldfilename в новый файл, имя которого указано в newfilename.
Что делает отдел по эксплуатации и сопровождению ИС? Отвечает за сохранность данных (расписания копирования, копирование и пр.)... ЧТО ТАКОЕ УВЕРЕННОЕ ПОВЕДЕНИЕ В МЕЖЛИЧНОСТНЫХ ОТНОШЕНИЯХ? Исторически существует три основных модели различий, существующих между... Конфликты в семейной жизни. Как это изменить? Редкий брак и взаимоотношения существуют без конфликтов и напряженности. Через это проходят все... Живите по правилу: МАЛО ЛИ ЧТО НА СВЕТЕ СУЩЕСТВУЕТ? Я неслучайно подчеркиваю, что место в голове ограничено, а информации вокруг много, и что ваше право... Не нашли то, что искали? Воспользуйтесь поиском гугл на сайте:
|