Это небольшой файл-указатель на какой-то объект
Обновлено: 21.11.2024
C++ – это язык программирования общего назначения, обеспечивающий большую гибкость, когда речь идет о системном программировании и разработке приложений. Однако язык имеет некоторые подводные камни, требующие внимания разработчиков для обеспечения качественного программирования. В этой статье объясняется, как можно избежать десяти самых распространенных ошибок разработчиков C++.
У Ватрослава более 20 лет опыта программирования. Ему нравятся сложные, хорошо продуманные проекты, которые бросают вызов его страсти к решению проблем.
Как бы мы ни старались, освободить всю динамически выделенную память очень сложно. Даже если мы можем это сделать, это часто не застраховано от исключений. Давайте рассмотрим простой пример:
Если возникает исключение, объект «a» никогда не удаляется. В следующем примере показан более безопасный и короткий способ сделать это. Он использует auto_ptr, который устарел в C++11, но старый стандарт все еще широко используется. Если возможно, его можно заменить на C++11 unique_ptr или scoped_ptr из Boost.
Что бы ни случилось, после создания объекта «а» он будет удален, как только выполнение программы выйдет из области видимости.
Однако это был лишь простейший пример этой проблемы C++. Есть много примеров, когда удаление должно выполняться в каком-то другом месте, возможно, во внешней функции или другом потоке. Вот почему следует полностью избегать использования пар new/delete и вместо этого использовать соответствующие интеллектуальные указатели.
Это одна из самых распространенных ошибок, которая приводит к утечкам памяти внутри производных классов, если внутри них выделена динамическая память. Бывают случаи, когда виртуальный деструктор нежелателен, т.е. когда класс не предназначен для наследования, а его размер и производительность имеют решающее значение. Виртуальный деструктор или любая другая виртуальная функция вводит в структуру класса дополнительные данные, т. е. указатель на виртуальную таблицу, которая увеличивает размер любого экземпляра класса.
Однако в большинстве случаев классы могут наследоваться, даже если это изначально не предполагалось. Поэтому рекомендуется добавлять виртуальный деструктор при объявлении класса. В противном случае, если класс не должен содержать виртуальные функции из соображений производительности, рекомендуется поместить комментарий в файл объявления класса, указывающий, что класс не должен наследоваться. Один из лучших способов избежать этой проблемы — использовать IDE, которая поддерживает создание виртуального деструктора во время создания класса.
Еще одно дополнение к теме — это классы/шаблоны из стандартной библиотеки. Они не предназначены для наследования и не имеют виртуального деструктора. Если, например, мы создадим новый расширенный строковый класс, который публично наследуется от std::string, существует вероятность того, что кто-то неправильно использует его с указателем или ссылкой на std::string и вызовет утечку памяти.
Чтобы избежать таких проблем C++, более безопасным способом повторного использования класса/шаблона из стандартной библиотеки является использование закрытого наследования или композиции.
Часто необходимо создавать временные массивы динамического размера. После того, как они больше не требуются, важно освободить выделенную память. Большая проблема здесь в том, что C++ требует специального оператора удаления со скобками [], о котором очень легко забывают. Оператор delete[] не просто удалит память, выделенную под массив, но сначала вызовет деструкторы всех объектов из массива. Также некорректно использовать оператор удаления без скобок [] для примитивных типов, даже если для этих типов нет деструктора. Для каждого компилятора нет гарантии, что указатель на массив будет указывать на первый элемент массива, поэтому использование удаления без квадратных скобок [] также может привести к неопределенному поведению.
Использование интеллектуальных указателей, таких как auto_ptr, unique_ptr , shared_ptr, с массивами также некорректно. Когда такой интеллектуальный указатель выходит из области действия, он вызывает оператор удаления без скобок [], что приводит к тем же проблемам, что и описанные выше. Если для массива требуется использование интеллектуального указателя, можно использовать scoped_array или shared_array от Boost или специализацию unique_ptr.
Если функциональность подсчета ссылок не требуется, что в основном относится к массивам, наиболее элегантным способом является использование вместо этого векторов STL. Они не только освобождают память, но и предлагают дополнительные функции.
В основном это ошибка новичка, но о ней стоит упомянуть, так как многие устаревшие коды страдают от этой проблемы. Давайте посмотрим на следующий код, в котором программист хотел провести некоторую оптимизацию, избегая ненужного копирования:
Объект «сумма» теперь будет указывать на локальный объект «результат». Но где находится объект «результат» после выполнения функции SumComplex? Нигде.Он находился в стеке, но после возврата функции стек был развернут, и все локальные объекты из функции были уничтожены. В конечном итоге это приведет к неопределенному поведению даже для примитивных типов. Чтобы избежать проблем с производительностью, иногда можно использовать оптимизацию возвращаемого значения:
Для большинства современных компиляторов, если строка возврата содержит конструктор объекта, код будет оптимизирован, чтобы избежать ненужного копирования — конструктор будет выполняться непосредственно над объектом «сумма».
Эти проблемы C++ возникают чаще, чем вы думаете, и обычно наблюдаются в многопоточных приложениях. Давайте рассмотрим следующий код:
В этом примере, если оба потока используют один и тот же идентификатор соединения, это приведет к неопределенному поведению. Ошибки нарушения прав доступа часто очень трудно найти.
В этих случаях, когда к одному и тому же ресурсу обращается более одного потока, очень рискованно сохранять указатели или ссылки на ресурсы, поскольку какой-то другой поток может их удалить. Гораздо безопаснее использовать умные указатели с подсчетом ссылок, например, shared_ptr от Boost. Он использует атомарные операции для увеличения/уменьшения счетчика ссылок, поэтому он является потокобезопасным.
Нечасто возникает необходимость вызывать исключение из деструктора. Даже в этом случае есть лучший способ сделать это. Однако исключения в большинстве случаев не выбрасываются из деструкторов явным образом. Может случиться так, что простая команда для регистрации уничтожения объекта вызовет генерацию исключения. Рассмотрим следующий код:
В приведенном выше коде, если исключение возникает дважды, например, во время уничтожения обоих объектов, оператор catch никогда не выполняется. Поскольку параллельно возникают два исключения, независимо от того, относятся ли они к одному типу или к разным, среда выполнения C++ не знает, как с этим справиться, и вызывает функцию завершения, которая приводит к прекращению выполнения программы.
Итак, общее правило таково: никогда не позволяйте исключениям покидать деструкторы. Даже если это уродливо, потенциальное исключение должно быть защищено следующим образом:
Шаблон auto_ptr устарел в C++11 по ряду причин. Он по-прежнему широко используется, так как большинство проектов все еще разрабатываются на C++98. У него есть определенная характеристика, которая, вероятно, знакома не всем разработчикам C++ и может вызвать серьезные проблемы у неосторожного человека. Копирование объекта auto_ptr приведет к передаче права собственности с одного объекта на другой. Например, следующий код:
Никогда не используйте auto_ptr внутри контейнеров STL. Копирование контейнеров оставит исходные контейнеры с неверными данными. Некоторые алгоритмы STL также могут привести к аннулированию auto_ptr.
Никогда не используйте auto_ptr в качестве аргумента функции, так как это приведет к копированию и оставит недействительным значение, переданное в аргумент после вызова функции.
Если auto_ptr используется для элементов данных класса, обязательно сделайте правильную копию внутри конструктора копирования и оператора присваивания или запретите эти операции, сделав их закрытыми.
По возможности используйте какой-либо другой современный интеллектуальный указатель вместо auto_ptr.
На эту тему можно написать целую книгу. У каждого контейнера STL есть определенные условия, при которых он делает недействительными итераторы и ссылки. Важно помнить об этих деталях при использовании любой операции. Как и предыдущая проблема C++, эта также может очень часто возникать в многопоточных средах, поэтому для ее предотвращения необходимо использовать механизмы синхронизации. В качестве примера рассмотрим следующий последовательный код:
С логической точки зрения код выглядит совершенно нормально. Однако добавление второго элемента в вектор может привести к перераспределению памяти вектора, что сделает недействительными итератор и ссылку и приведет к ошибке нарушения прав доступа при попытке доступа к ним в последних двух строках.
Возможно, вы знаете, что передавать объекты по значению — плохая идея, так как это влияет на производительность. Многие оставляют это так, чтобы не вводить лишние символы, или, возможно, думают вернуться позже, чтобы выполнить оптимизацию. Обычно это никогда не делается, и в результате получается менее производительный код и код, склонный к неожиданному поведению:
Этот код будет скомпилирован. Вызов функции «func1» создаст частичную копию объекта «b», т.е. скопирует только часть класса «A» объекта «b» в объект «a» («задача нарезки»). Таким образом, внутри функции он также будет вызывать метод из класса «А» вместо метода из класса «В», что, скорее всего, не соответствует ожиданиям тех, кто вызывает функцию.
Аналогичные проблемы возникают при попытке перехватить исключения. Например:
Когда из функции func2 выбрасывается исключение типа ExceptionB, оно будет перехвачено блоком catch, но из-за проблемы с нарезкой будет скопирована только часть из класса ExceptionA, будет вызван неверный метод, а также повторное генерирование вызовет неправильное исключение для внешнего блока try-catch.
Подводя итог, всегда передавайте объекты по ссылке, а не по значению.
Даже определяемые пользователем конверсии иногда очень полезны, но они могут привести к непредсказуемым конверсиям, которые очень трудно обнаружить. Допустим, кто-то создал библиотеку с классом строк:
Первый метод предназначен для создания строки длины n, а второй предназначен для создания строки, содержащей заданные символы. Но проблема начинается, как только у вас появляется что-то вроде этого:
В приведенном выше примере s1 станет строкой размером 123, а не строкой, содержащей символы «123». Второй пример содержит одинарные кавычки вместо двойных кавычек (что может произойти случайно), что также приведет к вызову первого конструктора и созданию строки очень большого размера. Это действительно простые примеры, и есть много более сложных случаев, которые приводят к путанице и непредсказуемым преобразованиям, которые очень трудно найти. Есть 2 общих правила, как избежать таких проблем:
Определите конструктор с ключевым словом absolute, чтобы запретить неявные преобразования.
Вместо использования операторов преобразования используйте явные методы диалога. Для этого требуется немного больше набора текста, но его гораздо проще читать, и он может помочь избежать непредсказуемых результатов.
Заключение
C++ — мощный язык. На самом деле, многие из приложений, которые вы используете каждый день на своем компьютере и полюбили, вероятно, созданы с использованием C++. Как язык, C++ предоставляет разработчику огромную гибкость благодаря некоторым наиболее сложным функциям, наблюдаемым в объектно-ориентированных языках программирования. Однако эти сложные функции или гибкие возможности часто могут стать причиной путаницы и разочарования для многих разработчиков, если они не используются ответственно. Надеюсь, этот список поможет вам понять, как некоторые из этих распространенных ошибок влияют на то, чего вы можете достичь с помощью C++.
В современном программировании на C++ стандартная библиотека включает интеллектуальные указатели, которые помогают гарантировать, что в программах отсутствуют утечки памяти и ресурсов, а также они защищены от исключений.
Использование интеллектуальных указателей
Интеллектуальные указатели определяются в пространстве имен std в заголовочном файле. Они имеют решающее значение для языка программирования RAII или Приобретение ресурсов – это инициализация. Основная цель этой идиомы — гарантировать, что получение ресурсов происходит одновременно с инициализацией объекта, чтобы все ресурсы для объекта создавались и подготавливались в одной строке кода. С практической точки зрения, основной принцип RAII состоит в том, чтобы передать право владения любым ресурсом, выделенным в куче, например, динамически выделяемой памятью или дескрипторами системных объектов, объекту, выделенному в стеке, деструктор которого содержит код для удаления или освобождения ресурса и также любой связанный код очистки.
В большинстве случаев, когда вы инициализируете необработанный указатель или дескриптор ресурса, чтобы он указывал на фактический ресурс, немедленно передайте указатель интеллектуальному указателю. В современном C++ необработанные указатели используются только в небольших блоках кода с ограниченной областью действия, циклах или вспомогательных функциях, где критична производительность и не возникает путаницы в отношении владельца.
В следующем примере объявление необработанного указателя сравнивается с объявлением интеллектуального указателя.
Как показано в примере, интеллектуальный указатель — это шаблон класса, который вы объявляете в стеке и инициализируете с помощью необработанного указателя, указывающего на объект, размещенный в куче. После того, как интеллектуальный указатель инициализирован, он владеет необработанным указателем. Это означает, что интеллектуальный указатель отвечает за удаление памяти, указанной необработанным указателем. Деструктор интеллектуального указателя содержит вызов для удаления, а поскольку интеллектуальный указатель объявлен в стеке, его деструктор вызывается, когда интеллектуальный указатель выходит за пределы области видимости, даже если где-то выше по стеку генерируется исключение.
Для доступа к инкапсулированному указателю используются знакомые операторы указателя -> и * , которые перегружаются классом интеллектуального указателя для возврата инкапсулированного необработанного указателя.
Всегда создавайте интеллектуальные указатели в отдельной строке кода, а не в списке параметров, чтобы не произошла незаметная утечка ресурсов из-за определенных правил распределения списка параметров.
В следующем примере показано, как тип интеллектуального указателя unique_ptr из стандартной библиотеки C++ можно использовать для инкапсуляции указателя на большой объект.
В этом примере показаны следующие основные шаги по использованию интеллектуальных указателей.
Объявите интеллектуальный указатель как автоматическую (локальную) переменную. (Не используйте выражение new или malloc для самого интеллектуального указателя.)
В параметре type укажите тип инкапсулированного указателя.
Передайте необработанный указатель на новый объект -ed в конструкторе интеллектуальных указателей. (Некоторые служебные функции или конструкторы интеллектуальных указателей делают это за вас.)
Для доступа к объекту используйте перегруженные операторы -> и *.
Разрешить интеллектуальному указателю удалить объект.
Умные указатели максимально эффективны как с точки зрения использования памяти, так и с точки зрения производительности. Например, единственным элементом данных в unique_ptr является инкапсулированный указатель. Это означает, что unique_ptr имеет точно такой же размер, как и этот указатель, либо четыре байта, либо восемь байтов. Доступ к инкапсулированному указателю с помощью перегруженных операторов * и -> интеллектуального указателя ненамного медленнее, чем прямой доступ к необработанным указателям.
Интеллектуальные указатели имеют свои собственные функции-члены, доступ к которым осуществляется с помощью "точечной" нотации. Например, некоторые интеллектуальные указатели стандартной библиотеки C++ имеют функцию-член сброса, которая освобождает право собственности на указатель. Это полезно, когда вы хотите освободить память, принадлежащую интеллектуальному указателю, до того, как интеллектуальный указатель выйдет за пределы области действия, как показано в следующем примере.
Интеллектуальные указатели обычно обеспечивают прямой доступ к необработанным указателям. Смарт-указатели стандартной библиотеки C++ имеют для этой цели функцию-член get, а CComPtr имеет общедоступный член класса p. Предоставляя прямой доступ к базовому указателю, вы можете использовать интеллектуальный указатель для управления памятью в собственном коде и по-прежнему передавать необработанный указатель в код, который не поддерживает интеллектуальные указатели.
Виды умных указателей
В следующем разделе приводится сводка различных типов интеллектуальных указателей, доступных в среде программирования Windows, и описывается, когда их использовать.
Умные указатели стандартной библиотеки C++
Используйте эти интеллектуальные указатели в качестве первого выбора для инкапсуляции указателей на простые старые объекты C++ (POCO).
unique_ptr
Допускает ровно одного владельца базового указателя. Используйте в качестве выбора по умолчанию для POCO, если вы точно не знаете, что вам требуется shared_ptr . Можно передать новому владельцу, но нельзя копировать или делиться. Заменяет auto_ptr, который устарел. Сравните с boost::scoped_ptr . unique_ptr маленький и эффективный; размер - один указатель, и он поддерживает ссылки rvalue для быстрой вставки и извлечения из коллекций стандартной библиотеки C++. Заголовочный файл: . Дополнительные сведения см. в разделе Как создавать и использовать экземпляры unique_ptr и класс unique_ptr.
shared_ptr
Умный указатель с подсчетом ссылок. Используйте, когда вы хотите назначить один необработанный указатель нескольким владельцам, например, когда вы возвращаете копию указателя из контейнера, но хотите сохранить оригинал. Необработанный указатель не удаляется до тех пор, пока все владельцы shared_ptr не выйдут из области действия или иным образом не откажутся от владения. Размер - два указателя; один для объекта и один для общего блока управления, который содержит счетчик ссылок. Заголовочный файл: . Дополнительные сведения см. в разделе Как создавать и использовать экземпляры shared_ptr и класс shared_ptr.
weak_ptr
Умный указатель особого случая для использования в сочетании с shared_ptr . Слабый_ptr обеспечивает доступ к объекту, который принадлежит одному или нескольким экземплярам shared_ptr, но не участвует в подсчете ссылок. Используйте, когда хотите наблюдать за объектом, но не требуете, чтобы он оставался живым. В некоторых случаях требуется для разрыва циклических ссылок между экземплярами shared_ptr. Заголовочный файл: . Дополнительные сведения см. в разделе Как создавать и использовать экземпляры weak_ptr и класс weak_ptr.
Умные указатели для COM-объектов (классическое программирование для Windows)
При работе с COM-объектами заключайте указатели интерфейса в соответствующий тип интеллектуального указателя. Библиотека активных шаблонов (ATL) определяет несколько интеллектуальных указателей для различных целей. Вы также можете использовать тип интеллектуального указателя _com_ptr_t, который компилятор использует при создании классов-оболочек из файлов .tlb. Это лучший выбор, если вы не хотите включать заголовочные файлы ATL.
Класс CComPtr
Используйте его, если вы не можете использовать ATL. Выполняет подсчет ссылок с помощью методов AddRef и Release. Дополнительные сведения см. в разделе Как создавать и использовать экземпляры CComPtr и CComQIPtr.
Класс CComQIPtr
Напоминает CComPtr, но также предоставляет упрощенный синтаксис для вызова QueryInterface для COM-объектов. Дополнительные сведения см. в разделе Как создавать и использовать экземпляры CComPtr и CComQIPtr.
Класс CComHeapPtr
Интеллектуальный указатель на объекты, использующие CoTaskMemFree для освобождения памяти.
Класс CComGITPtr
Интеллектуальный указатель для интерфейсов, полученных из глобальной таблицы интерфейсов (GIT).
_com_ptr_t Класс
Напоминает CComQIPtr по функциональности, но не зависит от заголовков ATL.
Интеллектуальные указатели ATL для объектов POCO
Помимо интеллектуальных указателей для COM-объектов, ATL также определяет интеллектуальные указатели и коллекции интеллектуальных указателей для простых старых объектов C++ (POCO).В классическом программировании для Windows эти типы являются полезными альтернативами коллекциям стандартной библиотеки C++, особенно когда переносимость кода не требуется или когда вы не хотите смешивать модели программирования стандартной библиотеки C++ и ATL.
Класс CAutoPtr
Интеллектуальный указатель, обеспечивающий уникальное владение путем передачи права собственности на копию. Сопоставимо с устаревшим классом std::auto_ptr.
Класс CHeapPtr
Интеллектуальный указатель для объектов, выделенных с помощью функции C malloc.
Класс CAutoVectorPtr
Интеллектуальный указатель для массивов, выделенных с помощью new[] .
Класс CAutoPtrArray
Класс, который инкапсулирует массив элементов CAutoPtr.
Класс CAutoPtrList
Класс, который инкапсулирует методы для управления списком узлов CAutoPtr.
Эта функция сохраняет указатель файла в двух значениях LONG. Для работы с указателями файлов, размер которых превышает одно значение LONG, проще использовать функцию SetFilePointerEx.
Синтаксис
Параметры
Дескриптор файла.
Описатель файла должен быть создан с правами доступа GENERIC_READ или GENERIC_WRITE. Дополнительные сведения см. в разделе Безопасность файлов и права доступа.
Младшие 32 разряда знакового значения, определяющие количество байтов, на которое нужно переместить указатель файла.
Если lpDistanceToMoveHigh не равно NULL, lpDistanceToMoveHigh и lDistanceToMove образуют одно 64-битное значение со знаком, указывающее расстояние для перемещения. р>
Если lpDistanceToMoveHigh имеет значение NULL, lDistanceToMove является 32-битным значением со знаком. Положительное значение для lDistanceToMove перемещает указатель файла вперед в файле, а отрицательное значение перемещает указатель файла назад.
[вход, выход, необязательно] lpDistanceToMoveHigh
Указатель на старшие 32 бита 64-битного расстояния со знаком, на которое нужно переместиться.
Если вам не нужны 32-битные старшие разряды, этот указатель должен быть установлен в NULL.
Если этот параметр не равен NULL, он также получает DWORD старшего порядка нового значения указателя файла. Дополнительные сведения см. в разделе "Примечания" в этой теме.
Начальная точка перемещения указателя файла.
Этот параметр может принимать одно из следующих значений.
Значение | Значение |
---|---|
FILE_BEGIN 0 | Начальная точка — ноль или начало файла. |
FILE_CURRENT 1 | Начальной точкой является текущее значение указателя файла. |
FILE_END 2 | Отправной точкой является текущая позиция конца файла. |
Возвращаемое значение
Если функция завершается успешно и lpDistanceToMoveHigh имеет значение NULL, возвращаемое значение является младшим DWORD нового указателя файла. Примечание. Если функция возвращает значение, отличное от INVALID_SET_FILE_POINTER, вызов SetFilePointer выполнен успешно. Вам не нужно вызывать GetLastError.
Если функция выполняется успешно и lpDistanceToMoveHigh не равно NULL, возвращаемое значение представляет собой младший DWORD нового указателя файла, а lpDistanceToMoveHigh содержит старший DWORD нового указателя файла. указатель файла.
Если функция завершается ошибкой, возвращается значение INVALID_SET_FILE_POINTER. Чтобы получить расширенную информацию об ошибке, вызовите GetLastError.
Если новый указатель файла имеет отрицательное значение, функция завершается ошибкой, указатель файла не перемещается, а GetLastError возвращает код ERROR_NEGATIVE_SEEK.
Если lpDistanceToMoveHigh имеет значение NULL и новая позиция в файле не соответствует 32-битному значению, функция завершается ошибкой и возвращает INVALID_SET_FILE_POINTER.
Примечание. Поскольку INVALID_SET_FILE_POINTER является допустимым значением младшего слова DWORD нового указателя файла, необходимо проверить как возвращаемое значение функции, так и код ошибки, возвращаемый GetLastError, чтобы определить, произошла ли ошибка. Если произошла ошибка, SetFilePointer возвращает значение INVALID_SET_FILE_POINTER, а GetLastError возвращает значение, отличное от NO_ERROR. Пример кода, демонстрирующий проверку на наличие сбоев, см. в разделе "Примечания" этой темы.
Примечания
Указатель файла, определяемый значением параметра hFile, не используется для перекрывающихся операций чтения и записи.
Параметр hFile должен указывать на файл, хранящийся на ищущем устройстве; например, том диска. Вызов функции SetFilePointer с дескриптором устройства, не осуществляющего поиск, такого как канал или устройство связи, не поддерживается, хотя функция SetFilePointer может не возвращать ошибку. Поведение функции SetFilePointer в этом случае не определено.
- Используйте элементы Offset и OffsetHigh структуры OVERLAPPED.
Будьте осторожны при установке указателя файла в многопоточном приложении. Вы должны синхронизировать доступ к общим ресурсам.Например, приложение с потоками, которые совместно используют дескриптор файла, обновляют указатель файла и читают из файла, должно защитить эту последовательность с помощью объекта критической секции или объекта мьютекса. Дополнительные сведения см. в разделе Объекты критической секции и объекты мьютекса.
Если дескриптор hFile открыт с установленным флагом FILE_FLAG_NO_BUFFERING, приложение может перемещать указатель файла только в положения, выровненные по секторам. Выровненная по сектору позиция — это позиция, которая является целым числом, кратным размеру сектора тома. Приложение может получить размер сектора тома, вызвав функцию GetDiskFreeSpace.
Если приложение вызывает SetFilePointer с расстоянием для перемещения значений, в результате чего позиция не выровнена по секторам, а дескриптор открывается с помощью FILE_FLAG_NO_BUFFERING, функция завершается ошибкой, а GetLastError возвращает ERROR_INVALID_PARAMETER.
Не является ошибкой установить указатель файла в позицию за концом файла. Размер файла не увеличивается, пока вы не вызовете функцию SetEndOfFile, WriteFile или WriteFileEx. Операция записи увеличивает размер файла до положения указателя файла плюс размер записанного буфера, что приводит к неинициализации промежуточных байтов.
Если возвращаемое значение — INVALID_SET_FILE_POINTER и если lpDistanceToMoveHigh не равно NULL, приложение должно вызвать GetLastError, чтобы определить, успешно или неудачно выполнена функция. В следующем примере кода показан этот сценарий.
Несмотря на то, что параметр lpDistanceToMoveHigh используется для управления большими файлами, значение этого параметра следует устанавливать при перемещении файлов любого размера. Если установлено значение NULL, то lDistanceToMove имеет максимальное значение 2^31–2, или на 2 гигабайта меньше 2, поскольку все значения указателя файла являются значениями со знаком. Поэтому, если есть даже небольшая вероятность того, что файл увеличится до этого размера, лучше всего обращаться с файлом как с огромным файлом и работать с 64-битными файловыми указателями. Благодаря сжатию файлов в файловой системе NTFS и разреженным файлам можно иметь большие файлы, даже если базовый том не очень велик.
Если lpDistanceToMoveHigh не равно NULL, то lpDistanceToMoveHigh и lDistanceToMove образуют одно 64-битное значение со знаком. Параметр lDistanceToMove обрабатывается как младшие 32 бита значения, а lpDistanceToMoveHigh — как старшие 32 бита, что означает, что lpDistanceToMoveHigh является расширением знака lDistanceToMove.
Чтобы переместить указатель файла с нуля на 2 гигабайта, для параметра lpDistanceToMoveHigh должно быть задано значение NULL или знаковое расширение lDistanceToMove. Чтобы переместить указатель более чем на 2 гигабайта, используйте lpDistanceToMoveHigh и lDistanceToMove как одну 64-битную величину. Например, для перемещения в диапазоне от 2 ГБ до 4 ГБ установите значение lpDistanceToMoveHigh равным нулю или –1 для расширения lDistanceToMove со знаком минус. р>
Для работы с 64-битными файловыми указателями вы можете объявить LONG, рассматривать его как верхнюю половину 64-битного файлового указателя и передать его адрес в lpDistanceToMoveHigh. Это означает, что вы должны рассматривать две разные переменные как логическую единицу, что может вызвать ошибку. Лучше всего использовать структуру LARGE_INTEGER для создания 64-битного значения и передачи двух 32-битных значений с помощью соответствующих элементов объединения.
Кроме того, лучше всего использовать функцию для скрытия интерфейса SetFilePointer. В следующем примере кода показан этот сценарий.
Вы можете использовать SetFilePointer для определения длины файла. Для этого используйте FILE_END для dwMoveMethod и найдите нулевое местоположение. Возвращаемое смещение файла — это длина файла. Однако такая практика может иметь непреднамеренные побочные эффекты, например невозможность сохранения текущего указателя файла, чтобы программа могла вернуться в это место. Вместо этого лучше использовать GetFileSize.
Вы также можете использовать функцию SetFilePointer для запроса текущей позиции указателя файла. Для этого укажите метод перемещения FILE_CURRENT и нулевое расстояние.
В Windows 8 и Windows Server 2012 эта функция поддерживается следующими технологиями.
Технология | Поддерживается |
---|---|
Протокол Server Message Block (SMB) 3.0 | Да |
SMB 3.0 Transparent Failover (TFO) | Да |
SMB 3.0 с масштабированием Общие файловые ресурсы (SO) | Да |
Файловая система общих томов кластера (CsvFS) | Да |
Отказоустойчивая файловая система (ReFS) | Да |
Примеры
Пример кода добавления файлов см. в разделе Добавление одного файла в другой файл.
Экземпляры этого класса поддерживают как чтение, так и запись в файл с произвольным доступом. Файл с произвольным доступом ведет себя как большой массив байтов, хранящийся в файловой системе.Существует разновидность курсора или индекса в подразумеваемом массиве, который называется указатель файла; операции ввода читают байты, начиная с указателя файла, и перемещают указатель файла дальше прочитанных байтов. Если файл произвольного доступа создан в режиме чтения/записи, то доступны и операции вывода; операции вывода записывают байты, начиная с указателя файла, и продвигают указатель файла за пределы записанных байтов. Операции вывода, которые записывают за текущий конец подразумеваемого массива, вызывают расширение массива. Указатель файла можно прочитать с помощью метода getFilePointer и установить с помощью метода seek.
В целом для всех подпрограмм чтения в этом классе справедливо то, что если конец файла достигается до того, как будет прочитано желаемое количество байтов, генерируется исключение EOFException (своего рода IOException ). Если какой-либо байт не может быть прочитан по какой-либо причине, кроме конца файла, генерируется исключение IOException, отличное от EOFException. В частности, исключение IOException может быть вызвано, если поток был закрыт.
Сводка конструктора
Создает файловый поток с произвольным доступом для чтения и, при необходимости, для записи в файл, указанный аргументом File.
Создает файловый поток с произвольным доступом для чтения и, при необходимости, для записи в файл с указанным именем.
Краткое описание метода
Модификатор и тип | Метод и описание |
---|---|
void | close() |
Устанавливает смещение указателя файла, измеряемое от начала этого файла, при котором происходит следующее чтение или запись.
Записывает b.length байтов из указанного массива байтов в этот файл, начиная с текущего указателя файла.
Преобразует аргумент типа double в тип long с помощью метода doubleToLongBits в классе Double , а затем записывает значение типа long в файл в виде восьмибайтового значения, старшим байтом вперед.
Преобразует аргумент типа float в тип int с помощью метода floatToIntBits в классе Float , а затем записывает значение типа int в файл в виде четырехбайтового числа, старшим байтом вперед.
Методы, унаследованные от класса java.lang.Object
Сведения о конструкторе
Файл произвольного доступа
Создает файловый поток с произвольным доступом для чтения и, при необходимости, для записи в файл с указанным именем. Создается новый объект FileDescriptor для представления подключения к файлу.
Аргумент mode указывает режим доступа, в котором файл должен быть открыт. Допустимые значения и их значения указаны для конструктора RandomAccessFile(File,String).
При наличии диспетчера безопасности его метод checkRead вызывается с аргументом имени в качестве аргумента, чтобы проверить, разрешен ли доступ для чтения к файлу. Если режим разрешает запись, метод менеджера безопасности checkWrite также вызывается с аргументом имени в качестве аргумента, чтобы проверить, разрешен ли доступ для записи в файл.
Файл произвольного доступа
Создает файловый поток с произвольным доступом для чтения и, при необходимости, для записи в файл, указанный аргументом File. Новый объект FileDescriptor создается для представления этого подключения к файлу.
Значение
< /tr> Значение
"r" Открыть только для чтения. Вызов любого из методов write результирующего объекта вызовет исключение IOException. "rw" Открыть для чтения и записи. Если файл еще не существует, будет предпринята попытка его создания. "rws" Открыт для чтения и записи, как и в случае с "rw", а также требует, чтобы каждое обновление содержимого файла или метаданных записывалось синхронно на базовое устройство хранения. "rwd" Открыть для чтения и записи, как с "rw", а также требуют, чтобы каждое обновление содержимого файла записывалось синхронно на базовое устройство хранения.
Режимы "rws" и "rwd" работают так же, как метод force(boolean) класса FileChannel, передавая аргументы true и false соответственно, за исключением того, что они всегда применяются к каждой операции ввода-вывода и поэтому часто более эффективны. Если файл находится на локальном запоминающем устройстве, то при возврате вызова метода этого класса гарантируется, что все изменения, внесенные в файл этим вызовом, будут записаны на это устройство. Это полезно для обеспечения того, чтобы важная информация не была потеряна в случае сбоя системы. Если файл не находится на локальном устройстве, такая гарантия не предоставляется.
Режим "rwd" можно использовать для уменьшения количества выполняемых операций ввода-вывода.Использование "rwd" требует только записи в хранилище обновлений содержимого файла; использование "rws" требует обновления как содержимого файла, так и его метаданных для записи, что обычно требует как минимум еще одной низкоуровневой операции ввода-вывода.
При наличии диспетчера безопасности его метод checkRead вызывается с указанием пути к файлу в качестве аргумента, чтобы проверить, разрешен ли доступ для чтения к файлу. Если режим разрешает запись, метод менеджера безопасности checkWrite также вызывается с аргументом пути, чтобы узнать, разрешен ли доступ для записи к файлу.
Сведения о методе
получить FD
получить канал
Позиция возвращаемого канала всегда будет равна смещению указателя файла этого объекта, возвращаемому методом getFilePointer. Изменение смещения указателя файла этого объекта, будь то явно или путем чтения или записи байтов, изменит положение канала и наоборот. Изменение длины файла с помощью этого объекта изменит длину, видимую через файловый канал, и наоборот.
Читает байт данных из этого файла. Байт возвращается как целое число в диапазоне от 0 до 255 (0x00-0x0ff). Этот метод блокируется, если вход еще не доступен.
Хотя RandomAccessFile не является подклассом InputStream , этот метод ведет себя точно так же, как метод InputStream.read() InputStream .
Считывает до len байт данных из этого файла в массив байтов. Этот метод блокируется до тех пор, пока не будет доступен хотя бы один байт ввода.
Хотя RandomAccessFile не является подклассом InputStream , этот метод ведет себя точно так же, как метод InputStream.read(byte[], int, int) InputStream .
Считывает до b.length байт данных из этого файла в массив байтов. Этот метод блокируется до тех пор, пока не будет доступен хотя бы один байт ввода.
Хотя RandomAccessFile не является подклассом InputStream , этот метод ведет себя точно так же, как метод InputStream.read(byte[]) InputStream .
Читать полностью
Читает b.length байтов из этого файла в массив байтов, начиная с текущего указателя файла. Этот метод многократно считывает файл до тех пор, пока не будет прочитано запрошенное количество байтов. Этот метод блокируется до тех пор, пока не будет прочитано запрошенное количество байтов, не будет обнаружен конец потока или не возникнет исключение.
Читать полностью
Читает ровно len байт из этого файла в массив байтов, начиная с текущего указателя файла. Этот метод многократно считывает файл до тех пор, пока не будет прочитано запрошенное количество байтов. Этот метод блокируется до тех пор, пока не будет прочитано запрошенное количество байтов, не будет обнаружен конец потока или не возникнет исключение.
пропустить байты
Этот метод может пропускать меньшее количество байтов, возможно, ноль. Это может быть результатом любого из ряда условий; достижение конца файла до того, как будут пропущены n байтов, — это только одна возможность. Этот метод никогда не генерирует исключение EOFException. Возвращается фактическое количество пропущенных байтов. Если n отрицательное, байты не пропускаются.
написать
написать
Записывает b.length байтов из указанного массива байтов в этот файл, начиная с текущего указателя файла.
написать
получить указатель файла
Устанавливает смещение указателя файла, измеряемое от начала этого файла, при котором происходит следующее чтение или запись. Смещение может быть установлено за пределами конца файла. Установка смещения за конец файла не меняет длину файла. Длина файла изменится только путем записи после того, как будет установлено смещение за конец файла.
длина
установить длину
Если текущая длина файла, возвращаемая методом length, больше аргумента newLength, файл будет усечен. В этом случае, если смещение файла, возвращенное методом getFilePointer, больше, чем newLength, то после возврата этого метода смещение будет равно newLength .
Если текущая длина файла, возвращаемая методом length, меньше аргумента newLength, файл будет расширен. В этом случае содержимое расширенной части файла не определяется.
закрыть
Закрывает этот файловый поток с произвольным доступом и освобождает все системные ресурсы, связанные с этим потоком. Закрытый файл с произвольным доступом не может выполнять операции ввода или вывода и не может быть повторно открыт.
Если у этого файла есть связанный канал, этот канал также закрывается.
readBoolean
Читает логическое значение из этого файла. Этот метод считывает один байт из файла, начиная с текущего указателя файла. Значение 0 представляет false . Любое другое значение представляет собой true . Этот метод блокируется до тех пор, пока не будет прочитан байт, не будет обнаружен конец потока или не возникнет исключение.
байт чтения
Читает восьмибитное значение со знаком из этого файла. Этот метод считывает байт из файла, начиная с текущего указателя файла.Если прочитан байт b , где 0 , то результат:
Этот метод блокируется до тех пор, пока не будет прочитан байт, не будет обнаружен конец потока или пока не возникнет исключение.
readUnsignedByte
Читает из этого файла восьмибитное число без знака. Этот метод считывает байт из этого файла, начиная с текущего указателя файла, и возвращает этот байт.
Этот метод блокируется до тех пор, пока не будет прочитан байт, не будет обнаружен конец потока или пока не возникнет исключение.
краткое чтение
Читает из этого файла 16-битное число со знаком. Метод считывает два байта из этого файла, начиная с текущего указателя файла. Если прочитаны два байта по порядку, это b1 и b2 , где каждое из двух значений находится в диапазоне от 0 до 255 включительно, тогда результат будет равен:
Этот метод блокируется до тех пор, пока не будут прочитаны два байта, не будет обнаружен конец потока или не возникнет исключение.
readUnsignedShort
Читает из этого файла 16-битное число без знака. Этот метод считывает два байта из файла, начиная с текущего указателя файла. Если считанные байты по порядку равны b1 и b2 , где 0 , то результат будет равен:
Этот метод блокируется до тех пор, пока не будут прочитаны два байта, не будет обнаружен конец потока или не возникнет исключение.
прочитатьсимвол
Читает символ из этого файла. Этот метод считывает два байта из файла, начиная с текущего указателя файла. Если считанные байты по порядку равны b1 и b2 , где 0 , то результат будет равен:
Этот метод блокируется до тех пор, пока не будут прочитаны два байта, не будет обнаружен конец потока или не возникнет исключение.
Чтение
Читает из этого файла 32-битное целое число со знаком. Этот метод считывает 4 байта из файла, начиная с текущего указателя файла. Если прочитаны байты по порядку: b1, b2, b3 и b4, где 0, то результат будет равен:
Этот метод блокируется до тех пор, пока не будут прочитаны четыре байта, не будет обнаружен конец потока или не возникнет исключение.
Долгое чтение
тогда результат равен:
Этот метод блокируется до тех пор, пока не будут прочитаны восемь байтов, не будет обнаружен конец потока или не возникнет исключение.
чтение с плавающей запятой
Читает число с плавающей запятой из этого файла. Этот метод считывает значение int, начиная с текущего указателя файла, как если бы он был методом readInt, а затем преобразует это целое число в число с плавающей запятой с помощью метода intBitsToFloat в классе Float .
Этот метод блокируется до тех пор, пока не будут прочитаны четыре байта, не будет обнаружен конец потока или не возникнет исключение.
Двойное чтение
Читает двойное значение из этого файла. Этот метод считывает длинное значение, начиная с текущего указателя файла, как будто с помощью метода readLong, а затем преобразует это длинное значение в двойное с помощью метода longBitsToDouble в классе Double .
Этот метод блокируется до тех пор, пока не будут прочитаны восемь байтов, не будет обнаружен конец потока или не возникнет исключение.
строка чтения
Читает следующую строку текста из этого файла. Этот метод последовательно считывает байты из файла, начиная с текущего указателя файла, пока не достигнет знака конца строки или конца файла. Каждый байт преобразуется в символ путем взятия значения байта для младших восьми битов символа и установки старших восьми битов символа равными нулю. Поэтому этот метод не поддерживает полный набор символов Unicode.
Строка текста заканчивается символом возврата каретки ( '\r' ), символом новой строки ( '\n' ), символом возврата каретки, за которым сразу следует символ новой строки, или концом файла . Символы конца строки отбрасываются и не включаются в возвращаемую строку.
Этот метод блокируется до тех пор, пока не будет прочитан символ новой строки, возврат каретки и байт, следующий за ним (чтобы узнать, является ли он новой строкой), не будет достигнут конец файла или не возникнет исключение.
прочитатьUTF
Читаются первые два байта, начиная с текущего указателя файла, как если бы это было readUnsignedShort . Это значение дает количество следующих байтов в закодированной строке, а не длину результирующей строки. Следующие байты затем интерпретируются как байты, кодирующие символы в модифицированном формате UTF-8, и преобразуются в символы.
Этот метод блокируется до тех пор, пока не будут прочитаны все байты, не будет обнаружен конец потока или пока не возникнет исключение.
написать логическое значение
Записывает логическое значение в файл как однобайтовое значение. Значение true записывается как значение (byte)1 ; значение false записывается как значение (byte)0 . Запись начинается с текущей позиции указателя файла.
запись байта
Записывает байт в файл как однобайтовое значение. Запись начинается с текущей позиции указателя файла.
написатьШорт
Записывает короткое замыкание в файл в виде двух байтов, старшим байтом вперед. Запись начинается с текущей позиции указателя файла.
написатьсимвол
Записывает char в файл как двухбайтовое значение, старший байт идет первым.Запись начинается с текущей позиции указателя файла.
записатьInt
Записывает в файл целое число в виде четырех байтов, старший байт идет первым. Запись начинается с текущей позиции указателя файла.
длинная запись
Записывает в файл длинное значение в виде восьми байтов, старшим байтом вперед. Запись начинается с текущей позиции указателя файла.
запись с плавающей запятой
Преобразует аргумент типа float в тип int с помощью метода floatToIntBits в классе Float , а затем записывает значение типа int в файл в виде четырехбайтового числа, старшим байтом вперед. Запись начинается с текущей позиции указателя файла.
написатьДвойной
Преобразует аргумент типа double в тип long с помощью метода doubleToLongBits в классе Double , а затем записывает это значение типа long в файл в виде восьмибайтового значения, старшим байтом вперед. Запись начинается с текущей позиции указателя файла.
записать байты
Записывает строку в файл в виде последовательности байтов. Каждый символ в строке записывается последовательно, отбрасывая его старшие восемь битов. Запись начинается с текущей позиции указателя файла.
написатьсимволы
Записывает в файл строку в виде последовательности символов. Каждый символ записывается в поток вывода данных как бы методом writeChar. Запись начинается с текущей позиции указателя файла.
записать UTF
Сначала в файл записываются два байта, начиная с текущего указателя файла, как если бы метод writeShort задавал количество следующих байтов. Это значение представляет собой количество фактически записанных байтов, а не длину строки. После длины каждый символ строки выводится последовательно, используя модифицированную кодировку UTF-8 для каждого символа.
- Обзор:
- Вложенный |
- Поле | |
- Подробности:
- Поле | |
Сообщите об ошибке или функции.
Дополнительные справочные материалы по API и документацию для разработчиков см. в документации по Java SE. Эта документация содержит более подробные описания, предназначенные для разработчиков, с концептуальными обзорами, определениями терминов, обходными путями и примерами рабочего кода.
Авторские права © 1993, 2020, Oracle и/или ее дочерние компании. Все права защищены. Использование регулируется условиями лицензии. Также ознакомьтесь с политикой распространения документации.
Читайте также: