如何在该引用超出范围后检测ptr是否仍引用有效引用

时间:2016-05-04 19:54:56

标签: c++ pointers iostream ostream

我正在玩弄溪流一点点,并且无法理解下面的内容。

这里我们有一个基本的ostream ptr设置为不同的输出流,无论是coutcerr还是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:我可以使用堆内存并使用智能指针做一些事情,但坦率地说,如果可能的话,我想保留这样的内容

6 个答案:

答案 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解决方案似乎是最好的解决方案。