@Konrad Rudolph is right, you can totally do this, easily, at least for cout/cerr/clog. You don't even need your own streambuf implementation, just use an ostringstream.
// Redirect cout.
streambuf* oldCoutStreamBuf = cout.rdbuf();
ostringstream strCout;
cout.rdbuf( strCout.rdbuf() );
// This goes to the string stream.
cout << "Hello, World!" << endl;
// Restore old cout.
cout.rdbuf( oldCoutStreamBuf );
// Will output our Hello World! from above.
cout << strCout.str();
Same thing works for cerr and clog, but in my experience that will NOT work for stdout/stderr in general, so printf won't output there. cout goes to stdout, but redirecting cout will not redirect all stdout. At least, that was my experience.
If the amount of data is expected to be small, the freopen/setbuf thing works fine. I ended up doing the fancier dup/dup2 thing redirecting to a pipe.
Update: I wrote a blog post showing the dup2 method I ended up using, which you can read here. It's written for OS X, but might work in other Unix flavors. I seriously doubt it would work in Windows. Cocoa version of the same thing here.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…