c++ - When destructors are called
- DQNOK (23/23) Oct 03 2007 I though destructors HAD TO BE CALLED when an object went out of
- Walter Bright (1/1) Oct 09 2007 If you could boil this down to a small test case, I'd appreciate it.
- Heinz Saathoff (31/41) Oct 16 2007 I'm not sure if the code is valid. You create a temporary 'mytype' and
- DQNOK (18/32) Oct 16 2007 temporary
- Heinz Saathoff (28/41) Oct 17 2007 Ok, didn't know this.
- DQNOK (6/10) Oct 17 2007 Great! Looks like maybe I just need to upgrade. I'm still
- DQNOK (15/19) Oct 17 2007 I just now downloaded the latest 8.5 (scppn 8.50.4n), and am still
- Heinz Saathoff (6/9) Oct 18 2007 dmc is only a 'driver program' that invokes the real compiler,
- Heinz Saathoff (12/18) Oct 18 2007 I just looked what the standard says abaut temporaries (12.2):
- Heinz Saathoff (44/44) Oct 18 2007 Hello again,
- DQNOK (29/56) Oct 22 2007 I don't think it's Foo() that should call the destructor, is it?
- Heinz Saathoff (16/53) Nov 01 2007 I have posted this question in the german usenet group=20
- DQNOK (50/78) Oct 17 2007 temporary
- Heinz Saathoff (9/27) Oct 18 2007 Hello,
- DQNOK (20/22) Oct 22 2007 Thanks, Heinz. I didn't know that.
I though destructors HAD TO BE CALLED when an object went out of scope. I realize that as long as the object is in scope, when the destructor is called is implementation dependent. (Well, I suppose that is still standard C++ behavior). I have code that really needs the destructor called when I want it called. I've tried { // create a new scope here printf( "%s", (char*)mytype(&x) ); //creates a mytype temporary } // end the scope here to force mytype::~mytype on the temporary //shouldn't be necessary because the temporary is out of scope //immediately after the printf call. but it's not working. The destructor is not being called until the end of the function. The mytype constructor alters x (inserts a null terminator for use with Standard C library functions), then the destructor puts it back to the way it was (removes the null terminator). Since the destructor is not be called immediately, x is being left altered and unuseable until after the function exits. Visual C++ calls the destructor immediately, and all is good. Digital Mars C++ waits to call the destructor and all is not good. Is this conforming behavior? Is there a way to force a destructor call? Thanks David
Oct 03 2007
If you could boil this down to a small test case, I'd appreciate it.
Oct 09 2007
DQNOK wrote...I have code that really needs the destructor called when I want it called. I've tried { // create a new scope here printf( "%s", (char*)mytype(&x) ); //creates a mytype temporary } // end the scope here to force mytype::~mytype on the temporary //shouldn't be necessary because the temporary is out of scope //immediately after the printf call.I'm not sure if the code is valid. You create a temporary 'mytype' and cast this object to a char*. Note that constructors don't return a value. I tried a small example with the cast an got this message: printf("my_type modified: %s\n", (char*)mytype(my_str)); ^ a.cpp(19) : Error: illegal cast from: mytype to : char *but it's not working. The destructor is not being called until the end of the function.Without the cast this works (but is still not portable): #include <stdio.h> struct mytype { mytype(char *s) : str(s) { prev_char = str[0]; str[0] = 'A'; } ~mytype() {str[0] = prev_char;} char *str; char prev_char; };//mytype int main() { char my_str[] = " new way to do it"; printf("my_type modified: %s\n", mytype(my_str)); printf("old again: %s\n", my_str); return 0; } This example relies on object layout and is therefore not portable. - Heinz
Oct 16 2007
== Quote from Heinz Saathoff (newshsaat arcor.de)'s articleDQNOK wrote...want itI have code that really needs the destructor called when Itemporarycalled. I've tried { // create a new scope here printf( "%s", (char*)mytype(&x) ); //creates a mytypetemporary} // end the scope here to force mytype::~mytype on theIt compiles and runs under both DMC, and VC, without any warnings. Under VC (using the debugger to track thru it) the destructor on the temporary is called immediately when it goes out of scope (right after printf is through with it). Under DMC, the temporary's destructor is not called until the end of the (main()) function.//shouldn't be necessary because the temporary is out of scope //immediately after the printf call.I'm not sure if the code is valid.You create a temporary 'mytype' and cast this object to a char*.I have overloaded the operator char*() and operator const char*() to return the correct character pointer member of mytype. It works fine.This example relies on object layout and is therefore notportable.- HeinzI'm not relying on object layout; I specify the "cptr" member when casting. Apparently it's still not portable anyway because I'm relying on the destructor to be called in a deterministic way. Thank you Heinz for your thoughts and response.
Oct 16 2007
Hello, DQNOK wrote...Ok, didn't know this.You create a temporary 'mytype' and cast this object to a char*.I have overloaded the operator char*() and operator const char*() to return the correct character pointer member of mytype. It works fine.As far as I know the actual standard is more precise in when the destructor must be called. I've added the operator char*() to may program and it runs with dmc (scppn version is 8.48.10n). This is the test prog: #include <stdio.h> struct mytype { mytype(char *s) : str(s) { prev_char = str[0]; str[0] = 'A'; } ~mytype() {str[0] = prev_char;} operator char* () { return str; } char prev_char; char *str; };//mytype int main() { char my_str[] = " new way to do it"; printf("my_type modified: %s\n", (char*)mytype(my_str)); printf("old again: %s\n", my_str); return 0; }This example relies on object layout and is therefore notportable.- HeinzI'm not relying on object layout; I specify the "cptr" member when casting. Apparently it's still not portable anyway because I'm relying on the destructor to be called in a deterministic way.Thank you Heinz for your thoughts and response.- Heinz
Oct 17 2007
As far as I know the actual standard is more precise in when the destructor must be called.I don't know what the standard says. Where can I find it (short of purchasing it from ISO or ANSI).I've added the operator char*() to may program and it runs with dmc (scppn version is 8.48.10n).Great! Looks like maybe I just need to upgrade. I'm still running 8.42. Thanks again, Heinz. David
Oct 17 2007
I just now downloaded the latest 8.5 (scppn 8.50.4n), and am still getting the earlier behavior of the destructor not being called until the function exits. Guess maybe I'll try to whittle my class down until it behaves like Heinz's class does, and discern what code makes it behave the way it currently is... Or maybe I'm just barking up the wrong tree! If C++ is free (by the standard) to call destructors when it pleases, then I just need to either: - redesign my code - or use a different compiler, and hope they don't choose to alter its destructor-call behavior. BTW, when I type "dmc ", the compiler still reports itself as version 8.42n even though scppn is 8.50.4n DavidI've added the operator char*() to may program and it runs with dmc (scppn version is 8.48.10n).Great! Looks like maybe I just need to upgrade. I'm still running 8.42.
Oct 17 2007
Hello, DQNOK wrote...BTW, when I type "dmc ", the compiler still reports itself as version 8.42n even though scppn is 8.50.4ndmc is only a 'driver program' that invokes the real compiler, optimizer, linker. scppn displays the version number of the real compiler. - Heinz
Oct 18 2007
Hello again, DQNOK wrote...Or maybe I'm just barking up the wrong tree! If C++ is free (by the standard) to call destructors when it pleases, then I just need to either: - redesign my code - or use a different compiler, and hope they don't choose to alter its destructor-call behavior.I just looked what the standard says abaut temporaries (12.2): "A temporary bound to a reference type parameter in a function call persists until the completion of the full expression containing the call." In your example it's not the temporary object but a returned value of a member function (operators are also meber functions). The full expression is the function call. So the destructor shall be called at the end of the function and before the next one is called, just as my example does. - Heinz
Oct 18 2007
Hello again, Just for your information, another problem when passing objects to ellipsis (as in printf). Compiling and running this code: class MyType { public: MyType(char *s) : str(s), len(strlen(s)) { printf("ctor\n"); } MyType(const MyType &m) : str(m.str), len(m.len) { printf("copy_ctor\n"); } ~MyType() { printf("dtor\n"); char *str; int len; private: MyType(); };//MyType void Foo(int x, ...) {} int main() { char c_str[] = "Ein C-String"; MyType mt(c_str); Foo(1, mt); return 0; } will give this output: As you see there is one destructor call missing. The reason is that Foo() should call the destructor but due to the ellipsis doesn't know what was passed to it as second parameter. PC-Lint explains it this way: Passing struct 'Symbol' to ellipsis -- A struct is being passed to a function at a parameter position identified by an ellipsis. For example: void g() { struct A { int a; } x; void f( int, ... ); f( 1, x ); ... } This is sufficiently unusual that it is worth pointing out on the likelihood that this is unintended. The situation becomes more severe in the case of a Non-POD struct [10]. In this case the behavior is considered undefined. - Heinz
Oct 18 2007
== Quote from Heinz Saathoff (newshsaat arcor.de)'s articleJust for your information, another problem when passing objects to ellipsis (as in printf). Compiling and running this code: class MyType { public: MyType(char *s) : str(s), len(strlen(s)) { printf("ctor\n"); } MyType(const MyType &m) : str(m.str), len(m.len) { printf("copy_ctor\n"); } ~MyType() { printf("dtor\n"); char *str; int len; private: MyType(); };//MyType void Foo(int x, ...) {} int main() { char c_str[] = "Ein C-String"; MyType mt(c_str); Foo(1, mt); return 0; } ... As you see there is one destructor call missing. The reason is that Foo() should call the destructor but due to the ellipsis doesn't know what was passed to it as second parameter.I don't think it's Foo() that should call the destructor, is it? Since it is main() that calls the constructor to make the temporary available for the call to Foo(), then it should be main () that also calls the destructor .. I think. At least, this is the way VC works (yeah, I know they're not the standard, but it makes sense that it should work this way). This looks to me like a possible error in DMC. Turns out, this is exactly what was causing the problem in my original code. I THOUGHT that the destructor call for the temporary was being deferred, but in fact, it was never being called at all! After enough printf's I finally figured it out. David begin 644 destructor1.cpp M<BIS*2`Z('-T<BAS*2QL96XH<W1R;&5N*',I*2`-"B` ('L ('!R:6YT9B B M("!->51Y<&4H8V]N<W0 37E4>7!E)B!M*3IS='(H;2YS='(I+&QE;BAM+FQE ` end
Oct 22 2007
Hello, DQNOK wrote ...=3D=3D Quote from Heinz Saathoff (newshsaat arcor.de)'s articleI have posted this question in the german usenet group=20 de.comp.lang.iso-c++ and got the answer that passing a non-POD to ... is undefined behaviour.=20 Falk Tannh=E4user quoted this text from the standard: [expr.call]/7: "When there is no parameter for a given argument, the argument is passed in such a way that the receiving function can obtain the=20 value of the argument by invoking va_arg. The lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard conversions=20 are performed on the argument expression. After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the program is ill-formed. If=20 the argument has a non-POD class type, the behavior is undefined." - HeinzJust for your information, another problem when passing objects to ellipsis (as in printf). Compiling and running this code: class MyType { public: MyType(char *s) : str(s), len(strlen(s)) { printf("ctor\n"); } MyType(const MyType &m) : str(m.str), len(m.len) { printf("copy_ctor\n"); } ~MyType() { printf("dtor\n"); char *str; int len; private: MyType(); };//MyType void Foo(int x, ...) {} int main() { char c_str[] =3D "Ein C-String"; MyType mt(c_str); Foo(1, mt); return 0; } ... As you see there is one destructor call missing. The reason is that Foo() should call the destructor but due to the ellipsis doesn't know what was passed to it as second parameter.=20 I don't think it's Foo() that should call the destructor, is it? Since it is main() that calls the constructor to make the temporary available for the call to Foo(), then it should be main () that also calls the destructor .. I think. At least, this is the way VC works (yeah, I know they're not the standard, but it makes sense that it should work this way). This looks to me like a possible error in DMC.
Nov 01 2007
== Quote from Heinz Saathoff (newshsaat arcor.de)'s articleI have posted this question in the german usenet group de.comp.lang.iso-c++ and got the answer that passing a non-POD to ... is undefinedbehaviour.== Quote from DQNOK (davidlqualls yahoo.com)'s articleWell, I don't believe that "undefined" and erroneous are the same thing.You are correct that they are not the same thing. There are erroneous constructs that are not undefined. But, undefined behavior means the compiler implementation has no requirements regarding what it must do. It doesn't need to issue a diagnostic and it doesn't have to behave in a sensible way (indeed, there may be no sensible way to behave). In other words, if your program has an undefined construct in it, you cannot count on what it may or may not do. The standard defines "undefined behavior": 1.3.12 undefined behavior [defns.undefined] behavior, such as might arise upon use of an erroneous program construct or erroneous data, for which this International Standard imposes no requirements. Undefined behavior may also be expected when this International Standard omits the description of any explicit definition of behavior. [Note: permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). Many erroneous program constructs do not engender undefined behavior; they are required to be diagnosed. ]Nov 14 2007== Quote from Heinz Saathoff (newshsaat arcor.de)'s articleDQNOK wrote...want itI have code that really needs the destructor called when Itemporarycalled. I've tried { // create a new scope here printf( "%s", (char*)mytype(&x) ); //creates a mytypetemporary} // end the scope here to force mytype::~mytype on the//shouldn't be necessary because the temporary is out of scope //immediately after the printf call.#include <stdio.h> struct mytype { mytype(char *s) : str(s) { prev_char = str[0]; str[0] = 'A'; } ~mytype() {str[0] = prev_char;} char *str; char prev_char; };//mytype int main() { char my_str[] = " new way to do it"; printf("my_type modified: %s\n", mytype(my_str)); printf("old again: %s\n", my_str); return 0; } - HeinzHeinz: I reworked your code a little to make it more reflective of my actual code. I've attached it but don't know if it'll come thru since I've never used attachments before. Surprisingly, it works as expected; the destructors are called when I think they should be called; just like in your code. Also very surprising is that there are NO CALLS TO THE COPY CONSTRUCTOR. I thought that when an object was passed as a function parameter (like to printf), the copy constructor was always called! ALWAYS CALLED! Therefore, to make things work correctly, my original copy constructor alerted the copy that it was in fact, a copy; a hack I needed to prevent the copy from misbehaving when it got destroyed. I'm thinking THIS may be the actual source of my mis-behaving code, not that the destructor is not being called. Is the compiler permitted to optimize away a copy-constructor call??? David begin 644 destructor.cpp M=')U8W0 ;7ET>7!E(`T*>R`-"B` ("!M>71Y<&4H8VAA<B`J<RD .B!S='(H M;&5N9R`](#HZ M("` ('L-"B` ("` ("` <')I;G1F*")<;FEN(&1E<W1R=6-T;W( 9F]R(%PB M='EP928 ;70I("` ("` ("` +R]C;W!Y(&-O;G-T<G5C=&]R+ T*("` ('L- M"B` ("` ("!P<FEN=&8H(EQN:6X 8V]P>2!C;VYS=')U8W1O<B!W:71H(%PB M<W1R*&EN="!O9G-T+"!I;G0 ;&5N*2\O82!N=6QL+71E<FUI;F%T960 <W5B M<W1R:6YG+ T*("` ('L-"B` ("` ("!M>71Y<&4 <F5T.PT*("` ("` (')E M="YL96YG(#T ;&5N.PT*("` ("` (')E="YS='( /2!S='(K;V9S=#L-"B` M("` ("!R970N<')E=E]T97)M(#T <F5T+G-T<EML96Y=.R`O+W)E;65M8F5R M,#L ("` ("` ("` ("` +R]N=6QL('1E<FUI;F%T92X-"B` ("` ("!R971U M<FX <F5T.PT*("` ('T-" T*("` (&]P97)A=&]R(&-H87(J(" I('L <F5T M.PT*("` (&-H87( <')E=E]T97)M.PT*?3LO+VUY='EP92`-" T*:6YT(&UA M('-U8G-T<FEN9SH 7"(E<UPB(BP *&-H87(J*2AM+G-U8EIS='(H,BPW*2DI M=&8H(EQN;W)I9VEN86P <W1R:6YG.B!<(B5S7"(B+"`H8VAA<BHI;2D[(`T* ` endOct 17 2007Hello, DQNOK wrote...Heinz: I reworked your code a little to make it more reflective of my actual code. I've attached it but don't know if it'll come thru since I've never used attachments before.Got it.Surprisingly, it works as expected; the destructors are called when I think they should be called; just like in your code.It also does here.Also very surprising is that there are NO CALLS TO THE COPY CONSTRUCTOR. I thought that when an object was passed as a function parameter (like to printf), the copy constructor was always called! ALWAYS CALLED! Therefore, to make things work correctly, my original copy constructor alerted the copy that it was in fact, a copy; a hack I needed to prevent the copy from misbehaving when it got destroyed. I'm thinking THIS may be the actual source of my mis-behaving code, not that the destructor is not being called. Is the compiler permitted to optimize away a copy-constructor call???Yes, constructor-optimization is allowed, but not required, in certain cases. I assume it's also allowed here (your second printf). It would't be allowed if you've passed 'm' directly. - HeinzOct 18 2007== Quote from Heinz Saathoff (newshsaat arcor.de)'s articleYes, constructor-optimization is allowed, but not required, in certain cases.Thanks, Heinz. I didn't know that. I'm in the process of redesigning my code to work regardless of whether the constructor gets called. For reasons like this issue we've been discussing (different compilers behaving differently), I abandoned C++ about a dozen years ago (before STL), and went to coding in straight C. My productivity soared once I stopped trying to "think in objects". Guess my brain is better wired for procedural programming. More recently though, I've been lured back to the dark side, and have been dabbling in OOP. There are nuances about C++ that I just didn't remember (like optimizing out constructor calls). I recently purchased "Effective C++" by Scott Meyers, and guess I'll be reading that too. Before C++ was all the rage, I eagerly anticipated PC Lint's ad in each issue of the C Users Journal. I always spotted the error within seconds. Once PC Lint supported C++ however, I could barely even understand their explanation of why the code was errant. Like I said, not wired for it (or maybe I'm just being lazy and don't want to learn it...)Oct 22 2007