验证背景 1 2 3 4 5 6 7 8 #define TO_STR(x) ((AnsiString)x).c_str() BOOL Dict::GetID (TcxComboBox *cb, UINT32 *pulID) { CHAR *szValue = TO_STR (cb->Text); return GetID (szValue, pulID); }
上面的代码中 TO_STR(x)
宏定义,我理解为通过 x
构造出一个临时 AnsiString
的对象, 并对该临时对象调用成员函数 c_str()
获得指向其内部的 const char *
。 由于匿名的临时对象的生命周期是该行结束即结束,所以获取到的 const char *
指针不可使用。 为了探究这个问题,使用以下代码来验证我的想法。
验证过程 由于 UnicodeString 不是标准库的对象,其源码繁杂且找不到手册,所以猜测 UnicodeString
与 AnsiString
的实现,分为两种:
分别单独实现
UnicodeString
继承自 AnsiString
分别单独实现 代码实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 #include <iostream> #include <string.h> #define LOG std::cout namespace gos{ template <class T>std::string to_string (const T* dec) { char acText[32 ]; sprintf (acText, "0x%X" , (size_t )dec); return acText; } } class AnsiString1 { public : AnsiString1 () { str = new char [5 ]; memset (str, 0 , sizeof (str)); LOG << "AnsiString1 构造函数 this: " << gos::to_string (this ) << ", str: " << gos::to_string (str) << '\n' ; } AnsiString1 (const AnsiString1& obj) { str = new char [5 ]; memset (str, 0 , sizeof (str)); sprintf (str, obj.str); LOG << "AnsiString1 拷贝构造函数 this: " << gos::to_string (this ) << ", new str(" << gos::to_string (str) << ")" << '\n' ; } ~AnsiString1 () { delete [] str; LOG << "~AnsiString1 析构函数 this: " << gos::to_string (this ) << ", delete str(" << gos::to_string (str) << ")" << '\n' ; } const char * c_str () { return str; } private : char * str; }; class UnicodeString1 { public : UnicodeString1 () { LOG << "UnicodeString1 构造函数 this: " << gos::to_string (this ) << '\n' ; } ~UnicodeString1 () { LOG << "~UnicodeString1 析构函数" << gos::to_string (this ) << '\n' ; } operator AnsiString1 () { LOG << "operator AnsiString1(): " << gos::to_string (this ) << '\n' ; return m; } private : AnsiString1 m; };
C++ Builder
运行结果 :
不会崩溃。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void test () { UnicodeString1 obj; const char * sz = ((AnsiString1)obj).c_str (); LOG << "使用字符串: " << gos::to_string (sz) << '\n' ; }
VS2010
运行结果 :
不会崩溃。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void test () { UnicodeString1 obj; const char * sz = ((AnsiString1)obj).c_str (); LOG << "使用字符串 sz: " << gos::to_string (sz) << '\n' ; AnsiString1& r = (AnsiString1)obj; const char * sz1 = r.c_str (); LOG << "使用字符串 sz1: " << gos::to_string (sz1) << '\n' ; }
VS2019
运行结果 :
不会崩溃。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void test () { UnicodeString1 obj; const char * sz = ((AnsiString1)obj).c_str (); LOG << "使用字符串 sz: " << gos::to_string (sz) << '\n' ; }
g++
运行结果
不会崩溃
1 2 3 4 5 6 7 8 9 10 11 12 void test () { UnicodeString1 obj; const char * sz = ((AnsiString1)obj).c_str (); LOG << "使用字符串 sz: " << gos::to_string (sz) << '\n' ; }
UnicodeString
继承自 AnsiString
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 #include <iostream> #include <string.h> #define LOG std::cout namespace gos{ template <class T>std::string to_string (const T* dec) { char acText[32 ]; sprintf (acText, "0x%X" , (size_t )dec); return acText; } } class AnsiString1 { public : AnsiString1 () { str = new char [5 ]; memset (str, 0 , sizeof (str)); LOG << "AnsiString1 构造函数 this: " << gos::to_string (this ) << ", str: " << gos::to_string (str) << '\n' ; } AnsiString1 (const AnsiString1& obj) { str = new char [5 ]; memset (str, 0 , sizeof (str)); sprintf (str, obj.str); LOG << "AnsiString1 拷贝构造函数 this: " << gos::to_string (this ) << ", new str(" << gos::to_string (str) << ")" << '\n' ; } ~AnsiString1 () { delete [] str; LOG << "~AnsiString1 析构函数 this: " << gos::to_string (this ) << ", delete str(" << gos::to_string (str) << ")" << '\n' ; } const char * c_str () { return str; } private : char * str; }; class UnicodeString1 : public AnsiString1{ public : UnicodeString1 () { LOG << "UnicodeString1 构造函数 this: " << gos::to_string (this ) << '\n' ; } ~UnicodeString1 () { LOG << "~UnicodeString1 析构函数" << gos::to_string (this ) << '\n' ; } operator AnsiString1 () { LOG << "operator AnsiString1(): " << gos::to_string (this ) << '\n' ; return *(AnsiString1*)this ; } };
C++ Builder
运行结果 :
不会崩溃。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void test () { UnicodeString1 obj; const char * sz = ((AnsiString1)obj).c_str () << '\n' ; LOG << "使用字符串: " << gos::to_string (sz) << '\n' ; }
VS2010
运行结果 :
不会崩溃。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void test () { UnicodeString1 obj; const char * sz = ((AnsiString1)obj).c_str (); LOG << "使用字符串 sz: " << gos::to_string (sz) << '\n' ; AnsiString1& r = (AnsiString1)obj; const char * sz1 = r.c_str (); LOG << "使用字符串 sz1: " << gos::to_string (sz1) << '\n' ; }
VS2019
运行结果 :
不会崩溃。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void test () { UnicodeString1 obj; const char * sz = ((AnsiString1)obj).c_str (); LOG << "使用字符串 sz: " << gos::to_string (sz) << '\n' ; }
g++
运行结果
不会崩溃
1 2 3 4 5 6 7 8 9 10 11 void test () { UnicodeString1 obj; const char * sz = ((AnsiString1)obj).c_str (); LOG << "使用字符串 sz: " << gos::to_string (sz) << '\n' ; }
结论
如果按照我们设想的 UnicodeString
的两种实现方式, 使用强制转换符, 在编译器 C++ Builder
、VS2010
、 VS2019
和 g++
都会构造临时对象 。
该临时对象在该行结束后马上析构。所以使用从临时对象中获取的指针可能会有问题,但C++ Builder
、VS2010
、 VS2019
和 g++
没有出现编译警告和运行崩溃 。
获取临时对象的引用在 C++ Builder
和 VS2010
中会有编译警告。在 VS2019
、g++
中会直接编译错误。