c ++,匿名(未命名)变量的对象生命周期

时间:2010-03-17 13:56:57

标签: c++

在下面的代码中,在'main()'的最后一行中构造的对象似乎在表达式结束之前被销毁。在'<<'之前调用析构函数被执行。这是怎么回事?

#include <string>
#include <sstream>
#include <iostream>

using std::string;
using std::ostringstream;
using std::cout;

class A : public ostringstream
{
public:
  A () {}
  virtual ~A ()
  {    
    string s;
    s = str();
    cout << "from A: " << s << std::endl;
  }
};

int
main ()
{
  string s = "Hello";
  A os;

  os << s;
  cout << os.str() << std::endl;

  A() << "checking this";
}

这是输出:

Hello
from A: 0x80495f7
from A: Hello

这是gdb日志:

(gdb) b os.cxx : 18
Breakpoint 1 at 0x80492b1: file os.cxx, line 18. (2 locations)
(gdb) r
Starting program: /home/joe/sandbox/test/os 
Hello

Breakpoint 1, ~A (this=0xbffff37c, __in_chrg=<value optimized out>, __vtt_parm=<value optimized out>) at os.cxx:18
18     cout << "from A: " << s << std::endl;
(gdb) p s.c_str ()
$1 = 0x804b45c "0x80495f7"
(gdb) p *s.c_str ()
$2 = 48 '0'
(gdb) c
Continuing.
from A: 0x80495f7

Breakpoint 1, ~A (this=0xbffff2bc, __in_chrg=<value optimized out>, __vtt_parm=<value optimized out>) at os.cxx:18
18     cout << "from A: " << s << std::endl;
(gdb) p s.c_str ()
$3 = 0x804b244 "Hello"
(gdb) p *s.c_str ()
$4 = 72 'H'
(gdb) c
Continuing.
from A: Hello

Program exited normally.
(gdb)

2 个答案:

答案 0 :(得分:4)

在执行完整语句之后才会删除A.

您遇到的问题不是由于A被删除和打印未经注册的数据引起的,而是由r值引用引起的。匿名实例只能通过值或const引用传递。

这意味着您的类将支持operator <<定义为成员函数,但不支持全局函数。

要显示此尝试

struct A {
    f()
};

void g(A & a) {
}

void foo() {
    A a;
    a.f();
    g(a);

    A().f();
    g(A());  // This does not compile
}

我实现了类似于你的类A的日志记录机制。它适用于Visual Studio 6但不适用于Visual Studio 2005.我使用委托而不是继承来修复它。

class A
{
    ostringstream mystream;
public:
  A () {}
  virtual ~A ()
  {    
    cout << "from A: " << mystream.str(); << std::endl;
  }
  ostream & stream()
  {
       return mystream;
  }
};

int
main ()
{
  string s = "Hello";
  A os;

  os.stream() << s;    
  A().stream() << "checking this";
}

我假设您计划将此用于日志记录,可能还有宏。这就是我如何根据上面的A使用我的课程。

#define TRACE_ERROR if (A::testLevel(A::Error) A(A::Error).stream()
#define TRACE_INFO if (A::testLevel(A::Info) A(A::Info).stream()

然后在代码中

void foo()
{
    int a = ..
    std::string s = ..
    TRACE_INFO << "Some information " << a << " message: " s;
}

答案 1 :(得分:2)

我相信你所看到的行为是因为“匿名临时不能作为非const引用传递给函数”的规则(不是真的不是,但在不同的编译器上有不确定的行为或不同的行为)。因此,它确实去了&lt;&lt;最后一行的运算符,但它找到&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;运算符而不是费用函数(const char *)重载,主要是因为上述规则。因此,构造临时A,并将其传递给&lt;&lt;&lt;返回非const引用的运算符。

因此,现在运算符&lt;(const void *)被定义为类的成员,而运算符&lt;(const char *)是一个自由函数。在非const临时函数上调用函数时,会在成员函数中查找与参数匹配的唯一函数,并且不会与其匹配任何自由函数。我知道MSVC与GCC有不同的行为。

实际上,如果你尝试将字符串“check this”更改为更小的东西,这样你就可以看到它的值(将它从char *转换为void *并查看你得到的值),你会发现它实际打印的是什么void * cast of“check this”。所以析构函数在最后被调用,但&lt;&lt;&lt;&lt;&lt;&lt;已将char *转换为void *,这就是打印的内容。