C ++ constructor and compiler automatically generate code traps, constructor Compiler
Recently debug various access violation projects, which are representative and can be solved by standard code standards.
The problem can be summarized into the following code:
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 }
In the above code, you can think that 'result' will point to 'teststr. m_value.c_str () ', because we reload 'operator const char *()',
Otherwise, if you run the above Code, you will find that 'result' points to a random memory address.
After I have ruled out that there are no problems around it, I opened the compilation code browser:
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)
Here, you can clearly see that the compiler has replaced 'teststr' and 'stringb' with temporary objects of the 'teststring' type, and then calls 'operator const char *() 'Come on, the result is converted to 'const char * ', but these two temporary objects are automatically destroyed, so the result you get becomes Dangling pointer.
As for the solution, you may think of the following changes:
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
You can explicitly call 'test. m_value (). c_str () 'to prevent the compiler from generating unexpected type conversions.
However, as I mentioned earlier in this article, this problem can be avoided through good code specifications. here we need to use 'explicit '. By defining a constructor with a parameter as 'explicit', we can avoid implicit calls by the compiler to the labeled constructor.
So the fix I suggest here is to define your TestString as follows:
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;};
Then let's take a look at the new code generated by the compiler:
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 .:)