C++建構函式和編譯器自動產生代碼的陷阱

來源:互聯網
上載者:User

標籤:glin   問題   方案   oca   彙編   value   explicit   word   res   

最近在項目中debug各種access violation的,其中這個問題比較有代表性,並且能夠被規範的代碼標準解決。

問題可以總結為以下的代碼:

 1 class TestString 2 { 3 public: 4     TestString(const char* input) : m_value(input) {} 5     TestString(const TestString& input) : m_value(input.m_value) {} 6     operator const char*() const { return m_value.c_str(); } 7     string m_value; 8 }; 9 10 void main()11 {12     TestString testStr("StringA");13     const char* stringB = "StringB";14     const char* result = true ? testStr : stringB;15     // Will result point to  "StringA"?16     assert(result == testStr.m_value.c_str());17 }

以上代碼裡面,你可以定認為`result`會指向`testStr.m_value.c_str()`吧,因為我們重載了`operator const char*()`,

其實不然,如果你運行以上的代碼,你會發現`result`最後指向的是一個“隨機”的記憶體位址。

我在排除了周圍沒有任何問題之後,開啟了彙編代碼的瀏覽器:

    const char* result = true ? testStr : stringB;00CE9585  mov         eax,1  00CE958A  test        eax,eax  00CE958C  je          main+0B0h (0CE95D0h)  00CE958E  lea         ecx,[testStr]  00CE9591  push        ecx  00CE9592  lea         ecx,[ebp-138h]  00CE9598  call        TestString::TestString (0CE1659h)  ...00CE95DA  call        TestString::TestString (0CE14DDh)  ...00CE9625  call        TestString::operator char const * (0CE105Ah)  ...00CE964C  call        TestString::~TestString (0CE14A1h)  ...00CE9670  call        TestString::~TestString (0CE14A1h)  

在這裡,你能夠清楚的看到編譯器把`testStr`和`stringB`都准換成了類型為`TestString`的臨時對象,然後調用`operator const char*()`來吧結果轉換為`const char*`,不過之後這2個臨時對象都被自動銷毀了,所以你得到的結果也成為了Dangling pointer。

至於解決方案,你估計可以想到這樣改:

    const char* result = true ? testStr.m_value.c_str() : stringB;0008504C  mov         eax,1  00085051  test        eax,eax  00085053  je          main+55h (085065h)  00085055  lea         ecx,[testStr]  00085058  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::c_str (081370h)  0008505D  mov         dword ptr [ebp-104h],eax  00085063  jmp         main+5Eh (08506Eh)  00085065  mov         ecx,dword ptr [stringB]  00085068  mov         dword ptr [ebp-104h],ecx  0008506E  mov         edx,dword ptr [ebp-104h]  00085074  mov         dword ptr [result],edx  

通過顯式的調用`test.m_value().c_str()`來避免編譯器產生預期之外的類型轉化。

不過記得我在本文開始說的,這個問題可以通過很好的代碼規範來避免,這裡我們需要用到的方法是`explicit`。通過把帶一個參數的建構函式定義為`explicit`,我們可以避免編譯器對被標記的建構函式的隱性調用。

所以這裡我所建議的fix是,這樣定義你的TestString:

class TestString{public:    explicit TestString(const char* input) : m_value(input) {}    explicit TestString(const TestString& input) : m_value(input.m_value) {}    operator const char*() const { return m_value.c_str(); }    string m_value;};

然後我們來看看編譯器產生的新代碼:

    const char* result = true ? testStr : stringB;00F23C3B  mov         eax,1  00F23C40  test        eax,eax  00F23C42  je          main+74h (0F23C54h)  00F23C44  lea         ecx,[testStr]  00F23C47  call        TestString::operator char const * (0F21604h)  00F23C4C  mov         dword ptr [ebp-110h],eax  00F23C52  jmp         main+7Dh (0F23C5Dh)  00F23C54  mov         ecx,dword ptr [stringB]  00F23C57  mov         dword ptr [ebp-110h],ecx  00F23C5D  mov         edx,dword ptr [ebp-110h]  00F23C63  mov         dword ptr [result],edx  

case close. :)

 

C++建構函式和編譯器自動產生代碼的陷阱

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.