文件输入和指针所有权语义
file input and pointer ownership semantics
我知道有两种方法可以将文件内容读取为 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.
}
编辑2:
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();
}
编辑 3:
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).
}
The reason I need a C-style string and not a unique_ptr is because I will be passing this data to a C function
你仍然可以获得一个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 没有开销。来自 Bjarne Stroustrup 的 "The C++ Programming Language":
unique_ptr is a very lightweight mechanism with no space or time overhead compared to correct use of a built-in pointer.
将文件逐行读入字符串的简单方法:
#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;
}
当您编写现代 C++ 时,有一组特定的情况需要使用指针,但这不是其中之一。当你不需要时,你应该总是不喜欢使用它们。 Return 价值优化 (RVO) 和 RAII 是很棒的东西!
默认情况下,在栈上创建的std::string
使用堆来存储其内部字符数据;因此在您的情况下无需担心堆栈内存有限。此外,std::string::c_str()
将 return 字符串的内部 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
现在将在其托管 GumboOutput
被销毁时(即超出范围时)自动调用 gumbo_destroy_output
。请自行决定使用它。
我知道有两种方法可以将文件内容读取为 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.
}
编辑2:
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();
}
编辑 3:
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).
}
The reason I need a C-style string and not a unique_ptr is because I will be passing this data to a C function
你仍然可以获得一个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 没有开销。来自 Bjarne Stroustrup 的 "The C++ Programming Language":
unique_ptr is a very lightweight mechanism with no space or time overhead compared to correct use of a built-in pointer.
将文件逐行读入字符串的简单方法:
#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;
}
当您编写现代 C++ 时,有一组特定的情况需要使用指针,但这不是其中之一。当你不需要时,你应该总是不喜欢使用它们。 Return 价值优化 (RVO) 和 RAII 是很棒的东西!
默认情况下,在栈上创建的std::string
使用堆来存储其内部字符数据;因此在您的情况下无需担心堆栈内存有限。此外,std::string::c_str()
将 return 字符串的内部 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
现在将在其托管 GumboOutput
被销毁时(即超出范围时)自动调用 gumbo_destroy_output
。请自行决定使用它。