繁体   English   中英

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

[英]c++, object life-time of anonymous (unnamed) variables

在下面的代码中,'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)

直到执行完整语句后,才会删除 A。

您遇到的问题不是由 A 被删除和打印未初始化的数据引起的,而是由 r 值引用引起的。 匿名实例只能通过值或常量引用传递。

这意味着您的类将支持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;
}

我相信您所看到的行为是因为“匿名临时对象不能作为非常量引用传递给函数”的规则(实际上并非如此,但在不同的编译器上具有未定义的行为或不同的行为)。 因此,它确实转到最后一行的 << 运算符,但它找到了 << 运算符的 member(const void*) 重载而不是自由函数 (const char*) 重载,主要是因为上述规则. 因此,临时 A 被构造,并传递给 << 运算符,该运算符返回非常量引用。

因此,现在 operator<<(const void*) 被定义为类的成员,而 operator<<(const char*) 是一个自由函数。 当在非常量临时函数上调用函数时,在成员函数中查找唯一与参数匹配的函数,并且没有与之匹配的自由函数。 我知道 MSVC 与 GCC 有不同的行为。

事实上,如果您尝试将字符串“checking this”更改为较小的字符串,以便您可以看到它的值(将其从 char* 转换为 void* 并查看您得到的值),您将看到它打印的内容实际上是 void* cast的“检查这个”。 所以析构函数在最后被调用,但 << 已将 char* 转换为 void* ,这就是打印的内容。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM