Ryan Smith
Ryan Smith

Reputation: 1335

Why does this code not switch cin and cout with file streams?

Below are two pieces of code. One works, one does not, I'd like to know why. I apologise in advance for the lack of comments and awful variable names but right now this language is really grinding my gears.

// File1.cpp (contains relevant includes)
// Works! It writes to out.txt and appears to use in.txt correctly
int main(int argc, char* argv[]) {
    int num;
    std::ifstream in("in.txt");
    std::streambuf* cinbuf = std::cin.rdbuf();
    std::cin.rdbuf(in.rdbuf());

    std::ofstream out("out.txt");
    std::streambuf* coutbuf = std::cout.rdbuf();
    std::cout.rdbuf(out.rdbuf());

    cout << "Give me a number: ";
    cin >> num;

    std::cin.rdbuf(cinbuf);
    std::cout.rdbuf(coutbuf);

    return 0;
}

// File2.cpp (contains relevant includes)
// Does not work! Outputs nothing to out.txt.
class TestWithStdIO {
        std::streambuf* cinbuf;
        std::streambuf* coutbuf;
    public:
        TestWithStdIO(const char* inFile, const char* outFile) {
            std::ifstream in(inFile);
            cinbuf = std::cin.rdbuf();
            std::cin.rdbuf(in.rdbuf());

            std::ofstream out(outFile);
            coutbuf = std::cout.rdbuf();
            std::cout.rdbuf(out.rdbuf());
        }
        ~TestWithStdIO() {
            std::cin.rdbuf(cinbuf);
            std::cout.rdbuf(coutbuf);
        }
};

int main(int argc, char* argv[]) {
    int num;
    TestWithStdIO* ioTest = new TestWithStdIO("in.txt", "out.txt");
    cout << "Give me a number: ";
    cin >> num;
    delete ioTest;

    return 0;
}

Upvotes: 1

Views: 510

Answers (2)

BoBTFish
BoBTFish

Reputation: 19767

The std::[io]fstreams in and out have local scope (or automatic storage duration) inside the TestWithStdIO constructor. They are destroyed (and the files closed), along with their contained buffers, at the end of the function, leaving you with dangling pointers insidecin and cout.

Consider instead making in and out members of TestWithStdIO, something like this:

class TestWithStdIO {
        std::streambuf* cinbuf;
        std::streambuf* coutbuf;

        std::ifstream in_;  // <-- member
        std::ofstream out_; // <-- member
    public:
        TestWithStdIO(const std::string& inFile, const std::string& outFile) :
            in_(inFile), out_(outFile) // <-- initializer list
        {
            cinbuf = std::cin.rdbuf();
            std::cin.rdbuf(in_.rdbuf());

            coutbuf = std::cout.rdbuf();
            std::cout.rdbuf(out_.rdbuf());
        }
        ~TestWithStdIO() {
            std::cin.rdbuf(cinbuf);
            std::cout.rdbuf(coutbuf);
        }
};

You might need in(inFile.c_str()) and the equivalent for out if you only have C++03 support.

Also, as pointed out in the comments, there is no need to dynamically allocate the TestWithStdIO instance, and in fact it is likely to be a source of bugs. Just do

TestWithStdIO ioTest ("in.txt", "out.txt");

Upvotes: 1

Oswald
Oswald

Reputation: 31657

In TestWithStdIO::TestWithStdIO(), The stream buffer pointed to by in.rdbuf() is destroyed together with in (i.e. at the end of the constructor).

Upvotes: 1

Related Questions