1

这段代码有什么问题以及如何解决?

int _tmain(int argc, _TCHAR* argv[])
{
std::ostream * o = &std::cout;
char text[4096];
char *file = "D://test.txt";

if ( file != NULL ) 
{
  strcpy ( text, file );
  strcat ( text, ".log" );
  o = & std::ofstream ( text );
}
*o << "test"; //Exception
return 0;
}
4

6 回答 6

4
o = & std::ofstream ( text );

右侧的表达式创建了一个临时地址,您将获得在表达式末尾被销毁的临时地址。之后使用o将调用未定义的行为。

你应该这样做:

{
   //...
   o = new std::ofstream ( text );
   if ( *o )
        throw std::exception("couldn't open the file");
}
//...

if  ( o != &std::cout )
   delete o; //must do this!
于 2011-08-05T16:32:29.477 回答
2

它不应该编译;该表达式std::ofstream( text )是一个右值(临时),C++ 不允许您获取&临时地址(运算符)。并且临时的生命周期只到完整表达式的结尾,所以只要你;在语句的末尾传递了它,它的析构函数就会被调用(并且它所在的内存可以用于其他事情)。

仅仅创建ofstream一个命名的局部变量也无济于事,因为变量的生命周期仅到声明它的块的末尾(下一个})。您必须在 std::ofstream之前定义if,然后打开它并o在 if 中设置,例如:

std::ofstream mayOrMayNotBeUsed;
if ( file != NULL ) {
    //  ...
    mayOrMayNotBeUsed.open( text );
    if ( !mayOrMayNotBeUsed.is_open() ) {
        //  Do something intelligent here...
    }
    o = &mayOrMayNotBeUsed;
}
于 2011-08-05T16:38:58.810 回答
1

问题是这段代码会导致未定义的行为:

o = & std::ofstream ( text );

当你写

std::ofstream ( text )

这会创建一个临时ostream对象,其生命周期会在它所在的语句完成执行后立即结束。当您获取它的地址并将其分配给指针o时,指针现在指向一个生命周期即将结束的临时对象。一旦语句完成执行,o现在就指向一个生命周期已经结束的对象,因此使用该对象具有未定义的行为。因此,当你写

*o << "test";

您正在尝试对死对象执行操作,从而导致问题。

要解决此问题,您应该

  1. 动态分配ofstreamby writing o = new std::ofstream(text);,它创建对象以使其生命周期超过语句的末尾,或者
  2. std::ofstream在顶部声明,_tmain以便它的生命周期延伸到函数的其余部分。

希望这可以帮助!

于 2011-08-05T16:32:18.087 回答
1

o = & std::ofstream ( text );这会创建一个临时的 ofstream 对象,其地址已分配给o但该对象立即被销毁,因此o指向已删除的对象。这应该有效(使用静态):

int _tmain(int argc, _TCHAR* argv[])
{
    std::ostream * o = &std::cout;
    char text[4096];
    char *file = "D://test.txt";

    if ( file != NULL ) 
    {
        strcpy ( text, file );
        strcat ( text, ".log" );
        static std::ofstream myofstream( text );
        o = &myofstream;
    }
    *o << "test"; //Exception
    return 0;
}
于 2011-08-05T16:32:43.200 回答
1

o = & std::ofstream ( text );

创建临时对象,o开始指向该对象的地址,稍后(在执行此行之后)该对象被销毁。因此未定义的行为(取消引用无效指针时)。

解决方案 - 创建它new

o = new std::ofstraem( text );

但不要忘记释放分配的内存,之前return

*o << "test";

if  ( &std::cout != o  ) // don't forget the check .. as I did at the first time
{
    o->close();  // not absolutely necessary, 
             // as the desctructor will close the file
    delete o;
}
return 0;
于 2011-08-05T16:33:29.650 回答
0

我担心你正在以一种非常不健康的方式混合 C 和 C++。

首先,我衷心推荐使用std::string而不是 a char*,相信我,你的麻烦会少得多。

其次,您应该注意指针:如果您不小心,它们可能会指向内存中不再托管任何“活动”对象的位置。

我会提出以下代码:

void execute(std::ostream& out) {
  out << "test\n";
} // execute

int main(int argc, char* argv[]) {
  if (argc == 1) {
    execute(std::cout);
    return 0;
  }

  std::string filename = argv[1];
  filename += ".log";

  std::ofstream file(filename.c_str());
  execute(file);
  return 0;
}

这说明了如何避免您陷入的两个陷阱:

  • 使用std::string我避免分配静态大小的缓冲区,因此我确实冒着缓冲区溢出的风险。此外,操作要容易得多。
  • 使用一个函数来提升打印逻辑,我取消了指针和它引入的微妙问题。

不幸的是,目前std::stringstd::fstream(和配偶)并没有很好地融合在一起。历史缺陷...如果我没记错的话,在 C++0x 中已修复。

于 2011-08-05T17:47:29.160 回答