我正在玩弄溪流一点点,并且无法理解下面的内容。
这里我们有一个基本的ostream ptr设置为不同的输出流,无论是cout
,cerr
还是file
。
// ostream ptr
std::ostream* outstream;
// set output ostream
void setOutput(std::ostream & os)
{
outstream = &os;
}
// write message to ostream
void writeData(const std::string & msg)
{
*outstream << msg << '\n';
}
int main (int argc, char * const argv[])
{
// init to std out
setOutput(std::cout);
writeData("message to cout");
setOutput(std::cerr);
writeData("message to cerr");
std::ofstream fileout("test.txt", std::ofstream::out | std::ofstream::app);
setOutput(fileout);
writeData("message to file");
//fileout.close();
setOutput(std::cout);
writeData("message2 to cout");
return 0;
}
以上工作完美,并显示了c ++ iostream实现的强度。完善。
但是,由于setOutput
是通过引用设置的,因此引用的对象必须保持在范围内。这就是问题出现的地方。如果ofstream或任何其他ostream无效,我想找出一种方法将输出默认为std::cout
。也就是说,引用的对象是或超出范围。
例如:
// write message to ostream
void writeData(const std::string & msg)
{
if (/*stream or memory is invalid*/)
setOutput(std::cout);
*outstream << msg << '\n';
}
// local fileout goes out of scope
void foo()
{
std::ofstream fileout("test.txt", std::ofstream::out | std::ofstream::app);
setOutput(fileout);
writeData("message to file");
}
int main (int argc, char * const argv[])
{
setOutput(std::cout);
writeData("message to cout");
foo();
/* problem the local fileout is no longer referenced by the ostream ptr*/
/* the following should be redirected to std::cout cuz of default*/
writeData("message2 to cout");
return 0;
}
上述情况很好,直到foo()
返回主函数。它出现了可怕的错误,因为本地定义的ofstream
不再可用。
显然这不可取,用户应该意识到这一点。但是我希望将所有这些包装在日志记录类中,从而保持对象的状态有效,即使这种滥用可能会发生。它会导致无效的访问冲突,很难找到。
具体问题。有没有办法弄清楚ostream ptr或任何ptr是否仍然引用有效的对象或内存位置?
ps:我可以使用堆内存并使用智能指针做一些事情,但坦率地说,如果可能的话,我想保留这样的内容
答案 0 :(得分:13)
具体问题。有没有办法弄清楚ostream ptr或任何ptr是否仍然引用有效的对象或内存位置?
没有。没有办法用原始指针来解决这个问题。至少不是标准的c ++。
只要指向对象,就需要保证指向的对象保持活着状态。
用于提供该保证的常见模式是RAII,详见其他答案。保证指针有效性的另一种方法是使用智能指针而不是原始指针。但是,这些与自动变量不兼容。
只要能保证指针不被解除引用,就可以继续指向死对象。这通常很难保证,因为如前所述,没有办法测试指向的对象是否存在。
答案 1 :(得分:10)
这听起来像是RAII的一个很好的用例。
编写一个以文件名和std::ostream**
为参数的类到其构造函数。在所述类的构造函数中,构造ofstream(作为成员),并将指针设置为ofstream。在析构函数中,恢复为stdout
。
然后,使用新类的声明替换以下函数的前两行。
void foo()
{
std::ofstream fileout("test.txt", std::ofstream::out | std::ofstream::app);
setOutput(fileout);
writeData("message to file");
}
答案 2 :(得分:5)
您应该使用RAII强制正确设置流,然后在对象被销毁时设置回std :: cout。
class OutputStream
{
protected:
static std::ostream*& internalGlobalStateOfOutputStream()
{
static std::ostream* out = &std::cout;
return out;
}
public:
static std::ostream& getOutputStream()
{
return *internalGlobalStateOfOutputStream();
}
};
template<typename T>
class OutputStreamOwner: public OutputStream
{
T ownedStream;
public:
OutputStreamOwner(T&& obj)
: ownedStream(std::move(obj))
{
internalGlobalStateOfOutputStream() = &ownedStream;
}
template<typename... Args>
OutputStreamOwner(Args... args)
: ownedStream(args...)
{
internalGlobalStateOfOutputStream() = &ownedStream;
}
~OutputStreamOwner()
{
internalGlobalStateOfOutputStream() = & std::cout;
}
// Delete copy
OutputStreamOwner(OutputStreamOwner const&) = delete;
OutputStreamOwner& operator(OutputStreamOwner const&) = delete;
};
用法是:
void foo()
{
OutputStreamOwner<std::ofstream> output("test.txt", std::ofstream::out | std::ofstream::app);
writeData("message to file");
}
答案 3 :(得分:3)
一种可能的方法是创建一个RAII类,在将流传递给setOutput之前将其包装起来。此类应设计为像shared_ptr一样工作,以便维护共享引用计数。然后writeData检查它是否只有剩余的引用,如果是,则销毁ostream并默认为cout。
答案 4 :(得分:2)
您可以使用将流作为输入的功能完全避免这些复杂情况。
void writeData(std::ostream& os, const std::string & msg)
{
os << msg << '\n';
}
您可以通过返回流来进一步优化它,以允许一个链接调用它:
std::ostream& os writeLine(std::ostream& os, const std::string & msg)
{
os << msg << '\n';
return os;
}
// declare stream
stream << writeLine(stream, "Foo") << writeLine(stream, "Bar");
事实上,此功能更好,更易于维护,因为您不必记住在任何给定时间设置的流。对于大型项目,这是一个重要的质量。
答案 5 :(得分:1)
具体问题。有没有办法弄清楚是否有ostream ptr或任何ptr仍然引用一个有效的对象或 记忆位置?
ps:我可以使用堆内存并使用智能指针执行某些操作 坦率地说,如果可能的话,我想保留这样的话
不,没有标准的方法来测试原始指针或引用是否仍然引用有效对象。
RAII是解决此类问题的标准C ++解决方案,因此在我看来,您应该关注智能指针。我不知道任何库提供的智能指针可以解决这个特定的问题,但基于共享所有权的RAII解决方案似乎是最好的解决方案。