文件输入和指针所有权语义

时间:2015-03-26 23:08:57

标签: c++ file pointers input unique-ptr

我知道将文件内容读取为C风格的字符串有两种方法。我需要一个C风格的字符串而不是unique_ptr<string>的原因是因为我将这个数据传递给一个C函数gumbo_parse(),其参数是一个C风格的字符串,这样我就避免了unique_ptr<string>的开销。我愿意反对我的决定。

std::ifstream ifs(bf::path("..."));                                              // Input file stream

第一种方式:

std::filebuf * ptr_fbuff = ifs.rdbuf();                                         // Buffer to file contents.
std::size_t fbuff_size = ptr_fbuff->pubseekoff(0, ifs.end, std::ios_base::in);  // Get offset from 0 to end of buffer.
ptr_fbuff->pubseekpos(0, std::ios_base::in);                                    // Move buffer position back to beginning.
char * buffer = new char[fbuff_size];                                           // Buffer to store file data.
ptr_fbuff->sgetn(buffer, fbuff_size);                                           // Get file data.

第二种方式:

std::stringstream sstm_buffer;                                                  // Buffer to file contents.
sstm_buffer << ifs.rdbuf();                                                     // Read file contents to sstm.
const std::string & str_buffer = sstm_buffer.str();                             // Get underlying string from sstm.
char * buffer = new char[str_buffer.size()];                                    // Buffer to store file data.
str_buffer.copy(buffer, str_buffer.size());                                     // Copy file contents to buffer.

做点什么:

GumboOutput * ptr_outpt = gumbo_parse(buffer);
//...

关闭文件:

gumbo_destroy_output(&kGumboDefaultOptions, ptr_outpt);
ifs.close();                                                                    // Close stream to file.
delete[] buffer;                                                                // Delete allocated buffer.

两者在内存成本和速度方面有何不同。显然,在将内容放入C风格的字符串之前,使用Stringstream作为缓冲区,而在将内容放入C风格的字符串之前,另一个使用filebuf。

现在回答我的第二个问题。所有权语义和内存分配。 ifstream是否为堆中为rdbuf返回的缓冲区分配了任何内存?我负责删除rdbuf返回的缓冲区吗?如果它没有,请说我正在阅读一个巨大的文件......是不是可能有很多数据要存储在堆栈中?

编辑:

std::unique_ptr<std::string> mytype::my_func(const std::string & path)
{
    std::ifstream ifs(path);                                            // Input file stream
    std::stringstream sstm_buffer;                                      // Buffer to file contents.
    sstm_buffer << ifs.rdbuf();                                         // Read file contents to sstm.
    ifs.close();                                                        // Close stream to file.
    auto ptr_buffer = std::make_unique<std::string>(sstm_buffer.str()); // Pointer to copy of sstm buffer contents.
    return std::move(ptr_buffer);                                       // Return pointer.
}

EDIT2:

std::string mytype::my_func(const std::string & path) const
{
    std::ifstream ifs(path);
    std::stringstream sstm_buf;
    sstm_buf << ifs.rdbuf();
    ifs.close();
    return sstm_buf.str();
}

EDIT3:

std::string psclient::file_get(const std::string & path) const
{
    std::ifstream ifs(path);    // Stream to file (Automatically close() on ~).
    std::ostringstream reader;  // Stream to read file contents.
    reader << ifs.rdbuf();      // Read in file contents.
    return reader.str();        // Return a move instead of copy (Done implicitly).
}

2 个答案:

答案 0 :(得分:2)

  

我需要一个C风格的字符串而不是unique_ptr的原因是因为我将这个数据传递给C函数

您仍然可以获得C风格的指针,例如

#include <iostream>
#include <string>
#include <memory>

void print_C_style_string(const char* psz)
{
    printf("str = %s", psz);
}

int main()
{
    std::unique_ptr<std::string> p{new std::string{"This is a string!"}};
    print_C_style_string(p.get()->c_str());
    return 0;
}

unique_ptr没有开销。来自&#34; C ++编程语言&#34;作者:Bjarne Stroustrup:

  

unique_ptr是一种非常轻量级的机制,与正确使用内置指针相比,没有空间或时间开销。

逐行读取文件到字符串的简单方法:

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main()
{
    ifstream fin("my_file.txt", ios::in);
    if(!fin){
        printf("Error opening file.");
        return 1;
    }

    while(fin.peek() != EOF){
        string s;
        getline(fin, s);
        cout << s << std::endl;
    }

    fin.close();

    return 0;
}

答案 1 :(得分:1)

当您编写现代C ++时,有一组特殊情况需要使用指针,但这不是其中之一。当你不必要时,你应该总是不要使用它们。返回值优化(RVO)和RAII是很棒的事情!

默认情况下,堆栈上创建的std::string使用堆来存储其内部字符数据;因此,在您的情况下,无需担心有限的堆栈内存。此外,std::string::c_str()将返回字符串的内部const char*数据,这正是gumbo_parse()接受的参数。

这简化了读取文件所需的代码。 RVO将防止不必要的副本,提高性能:

std::string get_file_contents(const std::string& filename)
{
    std::ifstream ifs{filename}; // Open the input file stream
    std::ostringstream ss;       // stringstream to retrieve file data
    ss << ifs.rdbuf();           // Read file data into ss buffer
    return ss.str();             // Return the ss buffer as string (RVO)
}

现在您不必担心明确释放任何内存。您可以使用上述功能并将结果字符串c_str()传递给gumbo_parse()

const std::string file_contents{get_file_contents("input.txt")};

GumboOutput* output = gumbo_parse(file_contents.c_str());

// ...

gumbo_destroy_output(&kGumboDefaultOptions, output);

如果你确实想在某个地方尝试使用智能指针,可以尝试使用自定义删除器将GumboOutput包装在一起;像这样的东西:

using namespace std::placeholders;  // for _1, _2, _3 ...
auto cleanup = std::bind(gumbo_destroy_output, &kGumboDefaultOptions, _1);
std::unique_ptr<int, decltype(cleanup)> output{
    gumbo_parse(file_contents.c_str()), cleanup };

unique_ptr现在会在其被管理的gumbo_destroy_output上自动调用GumboOutput(即,当它超出范围时)。请自行决定使用此功能。