c++, object life-time of anonymous (unnamed) variables
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.
精彩评论