Метод close ofstream также закрывает нижележащий дескриптор - PullRequest
0 голосов
/ 11 октября 2018

На платформе Windows дескриптор файла получается при вызове CreateFile, а затем этот дескриптор используется для инициализации объекта ofstream.Ниже приведен минимальный пример:

#include"stdafx.h"
#include <tchar.h>
#include <iostream>
#include <fstream>
#include <windows.h>
#include <io.h>
#include <fcntl.h>

class CSV_writer {
public:
    std::ofstream my_ofstream;
private:
    HANDLE my_handle = INVALID_HANDLE_VALUE;
    int file_descriptor = -1;
    FILE * my_file = nullptr;   //FILE type is actually a IO buff.
    const static unsigned int fl = 256;
public:
    explicit CSV_writer(const TCHAR * file_name_) {
    //get current directory
    TCHAR current_path[MAX_PATH];
    GetCurrentDirectory(MAX_PATH, current_path);

    TCHAR filename[fl]{ 0 };
    _tcscat_s(filename, file_name_);
    _tcscat_s(filename, _T(".csv"));
    if (current_path[_tcslen(current_path) - 1] != _T('\\') && _tcslen(current_path) < MAX_PATH - 1) {
        _tcscat_s(current_path, _T("\\"));
    }
    else {
        throw std::exception("path length exceeding limit.");
    }

    if (_tcslen(current_path) + _tcslen(filename) + 1 < MAX_PATH) {
        _tcscat_s(current_path, filename);
    }
    else {
        //current path exceeds the max path length defined in MAX_PATH
        throw std::exception("path length exceeding limit.");
    }

    this->my_handle = CreateFile(
        current_path,
        GENERIC_READ | GENERIC_WRITE,   //access permit, both read and write
        0,          //cannot be shared and cannot be opened again until the handle to the file or device is closed
        nullptr,    //returned handle can not be inherited by child process
        CREATE_ALWAYS,  //always create a new file, overwrite old one if it exists
        FILE_ATTRIBUTE_NORMAL,
        nullptr
    );

    if (my_handle != INVALID_HANDLE_VALUE) {
        int file_descriptor = _open_osfhandle((intptr_t)my_handle, _O_TEXT);
        if (file_descriptor != -1) {
            this->my_file = _fdopen(file_descriptor, "w");
            if (this->my_file != nullptr) {
                this->my_ofstream = std::ofstream(this->my_file);

            }
        }
    }
}

~CSV_writer() {
    // Closes stream, file, file_descriptor, and file_handle.
    this->my_ofstream.flush();
    this->my_ofstream.close();
    this->my_file = nullptr;
    this->file_descriptor = -1;
    this->my_handle = INVALID_HANDLE_VALUE;
}
};

int main(int argc, char* argv[])
{
    CSV_writer csv_writer(L"memory_layout");
    csv_writer.my_ofstream << "Type,\t" << "Size,\t" << "Offset,\t" <<   "Address\n";

    return 0;
}

Мой вопрос заключается в том, что после вызова «my_ofstream.close ()» впоследствии будет ли освобожден основной дескриптор файла?Или я должен вызвать Windows API CloseHandle () вручную после вызова close ()?

Обновление: для тех, кто говорит, что нет конструктора ofstream, принимающего FILE *, на самом деле, вроде как, see the screen shot below

1 Ответ

0 голосов
/ 11 октября 2018

Надеюсь, вы уже знаете, что используемый вами конструктор:

std::ofstream(FILE * fp)

- это нестандартное недокументированное расширение Microsoft, не гарантированное даже Microsoft.

В этом случаеMicrosoft не обещает вам даже, что:

int fd = ...;
...
FILE * fp = _fdopen(fd, "w");
...
std::osftream ofs(fp);
...
ofs.close();

сделает fclose(fp) - не берите в голову _close(fd).

Если, однако, вы примете это как данность, что ofs.close() fclose(fp) - и, очевидно, вы делаете - тогда Microsoft обещает вам, что это также _close(fd) документация

Примечания

...

Файловые дескрипторы, переданные в _fdopen, принадлежат возвращеннымФАЙЛ * поток.Если _fdopen успешен, не вызывайте _close для файлового дескриптора . Вызов fclose для возвращенного ФАЙЛА * также закрывает файловый дескриптор .

(Мой акцент.)

...