开发者

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

开发者 https://www.devze.com 2022-12-23 19:02 出处:网络
In the following code, the object constructed in the last line of \'main()\', seems to be destroyed before the end of the expression. The destructor is called before the \'<<\' is executed. Is t

In the following code, the object constructed in the last line of 'main()', seems to be destroyed before the end of the expression. The destructor is called before the '<<' is executed. Is this how it is supposed to be?

#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";
}

This is the output:

Hello
from A: 0x80495f7
from A: Hello

This is the gdb log:

(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 is not deleted until after the full statement is executed.

The problem you have is not caused by A being deleted and printing uninitalised data, but casued by r-value references. Annonymous instances can only be passed by value or const reference.

What this means is that your class will support operator << defined as member functions but not as global functions.

To show this try

struct A {
    f()
};

void g(A & a) {
}

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

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

I implemented a logging mechanism similar to your class A. It worked fine with Visual Studio 6 but not with Visual Studio 2005. I fixed it by using delegation instead of inheritance.

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";
}

I am assuming that you are planning to use this with logging and probably a macro. This is how I use my class based on A above.

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

then in code

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


I believe the behavior that you are seeing is because of the rule that "anonymous temporaries cannot be passed into functions as non-const references" (well not really NOT, but have undefined behavior or different behavior on different compilers). Thus, it does go to the << operator at the last line, but it finds the member(const void*) overload for the << operator rather than the free function (const char*) overload, mainly because of the rule stated above. Thus, a temporary A is constructed, and passed to the << operator which returns a non-const reference.

Thus, now the operator<<(const void*) is defined as a member of the class while operator<<(const char*) is a free function. When a function is called on a non-const temporary, the only function that matches the argument is looked up in the member functions and no free functions are matched to it. I know for a fact that MSVC has different behaviour to GCC.

Infact if you try changing the string "checking this" to something smaller so you can see its value (convert it from char* to void* and see what value you get), you will see that what it is printing is actually void* cast of "checking this". So the destructor is called at the very end, but the << has cast the char* to void* and that is what is printed.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号