在函数中返回ifstream

时间:2010-03-08 04:58:04

标签: c++ fstream ifstream ofstream

这对你来说可能是一个非常无聊的问题:如何(如果可能的话)我可以从函数中返回ifstream吗?

基本上,我需要从用户那里获取数据库的文件名,如果不存在具有该文件名的数据库,那么我需要为用户创建该文件。我知道怎么做,但只是要求用户在创建文件后重新启动程序。如果可能的话,我想避免给用户带来不便,但下面的函数不能在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;
}
编辑:对不起,忘了告诉你编译器错误在哪里以及是什么:

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 

编辑:我更改了函数以返回指针,而不是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

5 个答案:

答案 0 :(得分:14)

不,不是真的。 ifstream没有复制构造函数,如果你尝试返回一个,这意味着将函数中的实例复制到返回需要的地方。

通常的解决方法是传入一个引用,并在函数中修改该引用。

编辑:虽然这将允许您的代码工作,但它不会解决基本问题。现在,您将两个相当不同的职责混合到一个函数中:1)获取文件名,2)打开或创建该文件。我认为如果你将它们分开,代码会更简单,并且更容易消除你所看到的问题的根源。

编辑2:使用像这样的引用可以很好地工作,而不需要operator=。一般的想法是这样的:

int open_file(char const *name, fstream &stream) { 
    stream.open(name);
}

在这种情况下,赋值运算符既不必要也不有用 - 我们只需通过引用使用现有的fstream。当且仅当我们必须将参数传递给ctor时,才需要operator= 。使用流,我们可以默认构造一个不连接到文件的流,然后使用open连接到该文件。

答案 1 :(得分:5)

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
    }
    // ...
}

答案 2 :(得分:3)

ifstream不支持复制构造语义(即错误消息基本上是sais),因此您无法返回ifstream。返回一个ifstream *,然后向调用者传递删除分配指针的责任。

答案 3 :(得分:0)

此评论可能无法回答您的问题,我只想向@Corwin先生询问他的回答: 就像他的代码一样,我们有:getFileName块用于请求文件名,我认为我们应该像这样编码(这仅是我的看法):

void getFile(/*out*/ ifstream& file){
    string filename = getFileName();
    const bool file_exist = checkFileExistence(filename);
    if (!file_exist){
       ....
    }
    ....
}

int main()中,我认为:

int main(){
    ifstream file;
    getFile(file);
    if (file.is_open()){
        //some stuff
    }
}

这样,您可以从控制台上的用户输入获取filename

顺便说一句,感谢@Corwin先生的代码对我的帮助很大。

答案 4 :(得分:0)

作为一种选择,可以扩展ifstream并将自定义构造函数添加到新类中。

我已经对其进行扩展以创建测试资源流,并将测试资源查找封装在其中。

// 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());
}