ofstream 的 close 方法是否也关闭了基础句柄

Does the close method of ofstream also close the underlying handle

提问人:John Z. Li 提问时间:10/11/2018 最后编辑:John Z. Li 更新时间:10/11/2018 访问量:214

问:

在 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()”之后,底层文件句柄也会被释放吗?还是我必须在调用 close() 后手动调用 Windows API CloseHandle()?

更新:对于那些说没有采用 FILE* 的 ofstream 构造函数的人,实际上有,有点,see the screen shot below

C++ Windows IOstream

评论

1赞 Steve 10/11/2018
有一个构造函数,它需要一个参数吗?std::ofstreamFILE*
0赞 10/11/2018
@Steve我找不到这样的构造函数,甚至没有特定于Microsoft的构造函数。learn.microsoft.com/en-us/cpp/standard-library/......
0赞 Steve 10/11/2018
除非有,否则这个问题没有意义,因为你无论如何都不能以这种方式打开一个。
0赞 10/11/2018
你能发布一个最小的、完整的和可验证的例子以便人们可以帮助你吗?
0赞 10/11/2018
不要将代码作为链接发布。以文本形式发布代码。

答:

2赞 Mike Kinghan 10/11/2018 #1

我希望你已经知道你正在使用的构造函数:

std::ofstream(FILE * fp)

是一个非标准的、未记录的 Microsoft 扩展,即使是 Microsoft 也无法保证。

在这种情况下,Microsoft甚至不会向您承诺:

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

会做 - 没关系.fclose(fp)_close(fd)

但是,如果您认为它确实如此 - 显然您确实如此 - 那么Microsoft 确实向你保证它也会.从文档ofs.close()fclose(fp)_close(fd)

言论

...

传递到_fdopen的文件描述符由返回的 FILE * 流拥有。 如果_fdopen成功,请不要对文件描述符调用_close对返回的 FILE * 调用 fclose 也会关闭文件描述符

(我的重点。

评论

0赞 John Z. Li 10/11/2018
不错的文件挖掘。我的理由是 MS 添加扩展是有原因的,因此无需在创建句柄后先关闭句柄,然后使用其路径打开它即可使用它。我敢打赌,在 iostream 实例被销毁后,句柄会关闭。
1赞 Mike Kinghan 10/11/2018
@JohnZ.Li 我确信它和实验会证实这一点,但就我个人而言,我仍然不会让重要代码依赖于未记录的扩展。