C++ 在函数中返回 ifstream
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2399375/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Returning ifstream in a function
提问by wrongusername
Here's probably a very noobish question for you: How (if at all possible) can I return an ifstream from a function?
这对你来说可能是一个非常菜鸟的问题:我如何(如果可能的话)从函数返回一个 ifstream?
Basically, I need to obtain the filename of a database from the user, and if the database with that filename does not exist, then I need to create that file for the user. I know how to do that, but only by asking the user to restart the program after creating the file. I wanted to avoid that inconvenience for the user if possible, but the function below does not compile in gcc:
基本上,我需要从用户那里获取数据库的文件名,如果具有该文件名的数据库不存在,那么我需要为用户创建该文件。我知道怎么做,但只能要求用户在创建文件后重新启动程序。如果可能的话,我想避免给用户带来不便,但下面的函数不能在 gcc 中编译:
ifstream getFile() {
string fileName;
cout << "Please enter in the name of the file you'd like to open: ";
cin >> fileName;
ifstream first(fileName.c_str());
if(first.fail()) {
cout << "File " << fileName << " not found.\n";
first.close();
ofstream second(fileName.c_str());
cout << "File created.\n";
second.close();
ifstream third(fileName.c_str());
return third; //compiler error here
}
else
return first;
}
EDIT: sorry, forgot to tell you where and what the compiler error was:
编辑:抱歉,忘了告诉您编译器错误的位置和内容:
main.cpp:45: note: synthesized method ‘std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(const std::basic_ifstream<char, std::char_traits<char> >&)' first required here
EDIT: I changed the function to return a pointer instead as Remus suggested, and changed the line in main() to "ifstream database = *getFile()"; now I get this error again, but this time in the line in main():
编辑:我按照 Remus 的建议更改了函数以返回一个指针,并将 main() 中的行更改为“ifstream database = *getFile()”;现在我再次收到此错误,但这次在 main() 中:
main.cpp:27: note: synthesized method ‘std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(const std::basic_ifstream<char, std::char_traits<char> >&)' first required here
采纳答案by Corwin
bool checkFileExistence(const string& filename)
{
ifstream f(filename.c_str());
return f.is_open();
}
string getFileName()
{
string filename;
cout << "Please enter in the name of the file you'd like to open: ";
cin >> filename;
return filename;
}
void getFile(string filename, /*out*/ ifstream& file)
{
const bool file_exists = checkFileExistence(filename);
if (!file_exists) {
cout << "File " << filename << " not found." << endl;
filename = getFileName(); // poor style to reset input parameter though
ofstream dummy(filename.c_str();
if (!dummy.is_open()) {
cerr << "Could not create file." << endl;
return;
}
cout << "File created." << endl;
}
file.open(filename.c_str());
}
int main()
{
// ...
ifstream file;
getFile("filename.ext", file);
if (file.is_open()) {
// do any stuff with file
}
// ...
}
回答by Jerry Coffin
No, not really. ifstream
doesn't have a copy constructor, and if you try to return one, that means copying the instance in your function out to wherever the return needs to go.
不,不是真的。ifstream
没有复制构造函数,如果您尝试返回一个,这意味着将函数中的实例复制到需要返回的任何地方。
The usual workaround is to pass in a reference to one, and modify that reference in your function.
通常的解决方法是传入对一个的引用,并在您的函数中修改该引用。
Edit: while that will allow your code to work, it won't fix the basic problem. Right now, you're mixing two rather different responsibilities into a single function: 1) obtain a file name, 2) open or create that file. I think if you separate those, the code will be simpler, and make it much easier to eliminate the source of the problem you're seeing.
编辑:虽然这将允许您的代码工作,但它不会解决基本问题。现在,您将两个相当不同的职责混合到一个函数中:1) 获取文件名,2) 打开或创建该文件。我认为如果你把它们分开,代码会更简单,并且更容易消除你所看到的问题的根源。
Edit 2: Using a reference like this works perfectly well without an operator=
. The general idea is something like:
编辑 2:使用这样的参考可以在没有operator=
. 一般的想法是这样的:
int open_file(char const *name, fstream &stream) {
stream.open(name);
}
The assignment operator is neither necessary nor useful in this case -- we simply use the existing fstream via the reference. An operator=
would be necessary if and only ifwe had to pass the argument to the ctor. With a stream, we can default construct a stream that doesn't connect to a file, and then use open to connect to the file after the fact.
在这种情况下,赋值运算符既不必要也不有用——我们只是通过引用使用现有的 fstream。当且仅当我们必须将参数传递给 ctor 时,An才是operator=
必要的。使用流,我们可以默认构造一个不连接文件的流,然后在事后使用 open 连接到文件。
回答by Remus Rusanu
ifstream does not support copy construct semantics (that what the error message basically sais), so you cannot return an ifstream. Return an ifstream* instead, and pass to the caller the responsability to delete the allocate pointer.
ifstream 不支持复制构造语义(错误消息基本上是什么),因此您不能返回 ifstream。相反,返回一个 ifstream*,并将删除分配指针的责任传递给调用者。
回答by Q.H.Chu
This comment may not answer your question, I just want to ask Mr. @Corwin about his answer:
Just like his code, we have: getFileName
block for requesting filename, I think we should code like this (This is my opinion only):
这条评论可能没有回答你的问题,我只是想问@Corwin先生他的回答:就像他的代码一样,我们有:getFileName
请求文件名的块,我认为我们应该这样编码(这只是我的意见):
void getFile(/*out*/ ifstream& file){
string filename = getFileName();
const bool file_exist = checkFileExistence(filename);
if (!file_exist){
....
}
....
}
And in int main()
, I think :
而在int main()
,我认为:
int main(){
ifstream file;
getFile(file);
if (file.is_open()){
//some stuff
}
}
With this, you can get filename
from user input at the console.
有了这个,您可以filename
从控制台的用户输入中获取。
By the ways, thank Mr. @Corwin for the code its help me a lot.
顺便说一句,感谢@Corwin 先生的代码,它对我帮助很大。
回答by Nikita Karatun
As an option, ifstream may be extended and custom constructor added to new class.
作为一种选择,可以扩展 ifstream 并将自定义构造函数添加到新类中。
I've extended it to create test resource stream, encapsulating test resource lookup inside of it.
我已经扩展它来创建测试资源流,在其中封装测试资源查找。
// test_utils.h
class TestResourceStream : public std::ifstream {
public:
TestResourceStream(const char* file_path);
};
// test_utils.cpp
namespace fs = std::filesystem;
fs::path test_resource_path(const char* file_path) {
fs::path path{std::string{"tests/resources/"} + file_path};
if (!fs::exists(path))
throw std::runtime_error{std::string{"path "} +
fs::absolute(path).c_str() + " does not exist"};
return path;
}
TestResourceStream::TestResourceStream(const char* file_path)
:std::ifstream{test_resource_path(file_path).c_str()} {}
// usage in test
TEST_CASE("parse") {
std::list<GosDump::Expertise> expertises;
TestResourceStream stream("requests/page_response.json");
GosDump::Json::parse(expertises, stream);
REQUIRE(10 == expertises.size());
}