TITLE: Temporaries and internal data PROBLEM: Zen Kishimoto I wanted to get a internal data out of an object in a safe way. There is a new book called Effective C++ by Scott Meyers. He lists 50 pitfalls of C++ for the biginners. This has been very useful to me. Anyway, two of his items (21 and 29) dealt with this issue. His suggestion was to make the conversion operator defined as follows: class String { public: String(char *); operator const char *() const { return str; } private: char *str; }; I received two mails telling me that even the above type conversion is not safe and I should take a look at ARM 12.2 regarding creating temporary objects. Being a novice, I am totally confused. Is there a way to have access to a pointer in an object in a safe manner without making a copy of the pointed area? Any comments will be greatly appreciated. RESPONSE: sdm@cs.brown.edu (Scott Meyers), 27 Feb 92 Suppose the object you want a pointer into is call OBJ, and the pointer is called P. It should be fairly clear that if OBJ gets destroyed, P may no longer point to valid data. So if P has a longer lifetime than OBJ, you are on thin ice unless your type conversion operator makes a copy of the data and returns a pointer to the copy. Here's an example that turned out to be more interesting than I expected (see below): #include #include class String { private: char *str; public: String(const char *initValue) { str = new char[strlen(initValue)+1]; strcpy(str, initValue); cerr << "String created.\n"; } ~String() { delete [] str; cerr << "String destroyed.\n"; } operator const char *() const { return str; } }; String helloWorld() { return "Hello World"; } main() { cerr << "Entering main\n"; const char *greeting = helloWorld(); cout << greeting << '\n'; cerr << "Exiting main\n"; } The statement of interest is this one, in main: const char *greeting = helloWorld(); The function helloWorld returns a String object, but greeting is of type const char*, so the compiler will call the type conversion operator to convert the String into a const char*. However, the String returned by helloWorld is a temporary object, because it is the return value of a function. As an object, it must be destroyed (through a call to the String destructor) sometime -- but when? The language specification says it must be destroyed no later than the end of main, but it may be destroyed as soon as the end of the statement. Once it is destroyed, of course, you can no longer rely on the data that greeting points to. (Technically, the temporary String could be completely optimized away by an ambitious compiler, but let's overlook that fact right now.) In summary, the type conversion written above is "safe" if you know that the lifetime of OBJ is at least as long as that of P, but with temporary objects, you almost never know that. For those situations, you must return a pointer to a copy of the internal data, and if you do that, you may have to manually handle the timing of the destruction of the copy you make. What made the example unexpectedly interesting was these results from the above program: // Entering main Results for g++ 2.0 // String created. Temporary created // String destroyed. Temporary destroyed // Hello World It prints okay here, but it's unreliable data // Exiting main This looks fine. The temporary was destroyed quickly, but legally. // Entering main Results for cfront 2.1 and 3.0 // String created. Temporary created // String destroyed. Temporary destroyed // Hello World // Exiting main // String destroyed. ??? This looks odd -- only one constructor call, but TWO destructor calls. Perhaps the copy constructor is affecting things? So I added this to the above code: String::String(const String& s) { str = s.str; cerr << "String created (copy ctor)\n."; } Now I get these results: // Entering main Results for cfront 2.1 and 3.0 with copy ctor // String created. Temporary created // Hello World // Exiting main // String destroyed. Temporary destroyed This looks okay. The temporary was destroyed late, but legally. Overall, it looks like a bug in 2.1 and 3.0 if the copy constructor is not specified. Or is there a better explanation? [ Adding type conversion operators should be done with great care. It is more likely to introduce subtle errors, hence outweighing any syntactic benefits. -adc ]