2010-03-17 50 views
3

在下面的代碼中,'main()'最後一行中構造的對象似乎在表達式結束之前被銷燬。在執行'< <'之前調用析構函數。這是應該如何?C++,匿名(未命名)變量的對象生命期

#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

我相信你所看到的行爲是因爲該規則,即「匿名臨時工不能作爲非const引用傳遞給函數「(很不真實,但在不同的編譯器中有未定義的行爲或不同的行爲)。因此,它確實在最後一行轉到<運算符,但它發現運算符的成員(const void *)過載,而不是費用函數(const char *)過載,主要是因爲上述規則。因此,構造了一個臨時A,並將其傳遞給返回非const引用的運算符< <。

因此,現在運算符< <(const void *)被定義爲該類的成員,而運算符< <(const char *)是一個自由函數。當在非const的臨時對象上調用一個函數時,在成員函數中查找與該參數匹配的唯一函數,並且沒有自由函數與其匹配。我知道MSVC對GCC有不同的行爲。如果您嘗試將字符串「檢查」字符串更改爲更小的字符以便您可以看到其值(將其從char *轉換爲void *並查看您獲得的值),則會看到它正在打印的內容是實際上是無效的*「檢查這個」。所以析構函數在最後被調用,但是< <已經將char *轉換爲void *,並且這是打印的內容。

+0

很好的解釋,你貼當我回答。 – iain 2010-03-17 15:01:25

4

直到全部語句執行後才刪除A.

您擁有的問題不是由於被刪除並打印未初始化的數據而引起的,而是由r值引用造成的。 Annonymous實例只能由值或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; 
} 
+0

謝謝你們。我現在知道了。 – 2010-03-17 15:11:03