CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 2 12 LastLast
Results 1 to 15 of 21

Thread: Destruction???

  1. #1
    Join Date
    Feb 2005
    Location
    Denver
    Posts
    353

    Destruction???

    Examine the following code (don't worry about it's correctness???), it's just to illustrate a point...
    Code:
    #include <iostream>
    Code:
    #include <iomanip>
    
    using std::cout;
    
    using std::endl;
    
    using std::hex;
    
    using std::dec;
    
    
    
     
    class A
    {
    
    public:
    A () : _value (0) { cout << “\n A::A () executing: this = 0x” << this << endl; } A (const A& rhs) : _value (rhs._value) { cout << “\n A::A (const A&) executing: this = 0x” << this << endl; } ~A () { cout << “\n A::~A () executing: this = 0x” << this << endl; } A& operator++ () { cout << “\n A::operator++ () executing: this = 0x” << this << endl; ++_value; return *this; }
    private:
    int _value;
    }; int main () {
    cout << hex; A a; printf (“\n main: object = 0x%x”, ++a); cout << “\n\n End of main” << endl;
    }


    Execution of program...

    A::A () executing: this = 0xffbee4c8



    A:perator++ () executing: this = 0xffbee4c8



    A::A (const A&) executing: this = 0xffbee4c4



    main: object = 0xffbee4c4



    End of main



    A::~A () executing: this = 0xffbee4c8

    The execution of the copy constructor makes sense since the printf function probably requires a copy of the argument instead of a reference. So, assuming this is true, then why is the constuction of that object never destroyed???

  2. #2
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Destruction???

    The execution of the copy constructor makes sense since the printf function probably requires a copy of the argument instead of a reference.
    printf()) knows nothing about C++ objects.
    So, assuming this is true.
    The behavior is undefined as to what happens:
    Code:
    A a;
    printf (“\n main: object = 0x%x”, ++a);
    1) The printf() is expecting an integer to match the %x. You are sending an A object. This is undefined as to what printf() will do. It could have crashed your program.

    2) In C++, you cannot pass a non-POD object in a variable argument function.
    The printf() has the following prototype:
    Code:
    int printf(const char *format_string, ...)
    The "..." are the variable arguments. You cannot pass non-POD objects such as "A" there.

    So your program iinvokes undefined behavior.

    Regards,

    Paul McKenzie

  3. #3
    Join Date
    Feb 2005
    Location
    "The Capital"
    Posts
    5,306

    Thumbs up Re: Destruction???

    Quote Originally Posted by sszd
    ......don't worry about it's correctness???.....
    Believe me, it is important to worry about this.

    Why are you using printf as well as cout in your code? I would prefer sticking to one of these if I am writing any code and which one - > has been repeatedly discussed over here about the usage benefits of cout over printf. You could search for the thread using the Advance search link above. Paul's answer explains it all precisely and again (if I am not wrong). I ran your code on VC++ 6.0 and the output that I got is different from that of yours and this proves well the undefined behaviour:
    A::A () executing: this = 0x0012FF70
    A :: operator++ () executing: this = 0x0012FF70
    main: object = 0x1
    End of main
    A::~A () executing: this = 0x0012FF70
    I am not at all sure what you were trying to prove or see with the given piece of code? One objective is proved wrong above..If you want to see the creation of a copy and its deletion while passing arguments by value you could make your own function to verify that and not use printf. I assure you that it happens well and good and you may verify that. There would be no memory leaks or undeleted objects left at all that are created on the stack.

    Some inputs from my side (although this doesnt directly relate to your actual doubt but still you might find it interesting) : While overloading the '++' operator you need to be careful enough to provide both the overloads preincrement as well as postincrement because your class user might use it as in either ways. And when he/she does that you class isnt full proof to support it. That would be wrong behavior since your overloads should behave naturally as they usually/generally do. A sample implementation can be seen at this link..
    Last edited by exterminator; August 13th, 2005 at 05:10 AM.

  4. #4
    Join Date
    Feb 2005
    Location
    Denver
    Posts
    353

    Re: Destruction???

    Quote Originally Posted by Paul McKenzie
    printf()) knows nothing about C++ objects. The behavior is undefined as to what happens:
    Code:
    A a;
    printf (“\n main: object = 0x%x”, ++a);
    1) The printf() is expecting an integer to match the %x. You are sending an A object. This is undefined as to what printf() will do. It could have crashed your program.

    2) In C++, you cannot pass a non-POD object in a variable argument function.
    The printf() has the following prototype:
    Code:
    int printf(const char *format_string, ...)
    The "..." are the variable arguments. You cannot pass non-POD objects such as "A" there.

    So your program iinvokes undefined behavior.

    Regards,

    Paul McKenzie
    Yes, I agree completely with what you are saying. I agree that you cannot send non-POD objects to regular C functions. But that has nothing to do with the point I was trying to make with this experiment. In fact, I didn't even really care what value was returned by the printf (it just so happens on the platform I was using it was the address of the non-POD object created, and you're right, it could have just as easily crashed the program). What brought up my whole concern here is that the compiler (for one reason or another) decided there was a need to create a new object (obviously by using the copy ctor). Howvever, when the program ends, you see the original object getting destroyed but never see the other objects destruction. So I began thinking to myself, "gee, is this some sort of memory leak?", even though I believe it's on the stack as opposed to the heap. Nevertheless, memory is still being used that never appears to be getting released. Of course, if this were in a block of code as opposed to the program just exiting it would be even more of a concern (although I've heard of platforms that do not release memory even after a program ends). So again, if the compiler sees a need to create a new object, why does it not also see a need to destroy it? Even though the C function printf knows nothing about C++ objects, I believe the object is getting created by the compiler prior to the execution of the printf function due to the type of argument that C++ sees is being passed into the C function. So why, when the block of code the printf function is in (in this case the program itself) is the object not destroyed?
    Quote Originally Posted by exterminator
    Quote:
    Originally Posted by sszd
    ......don't worry about it's correctness???.....

    Believe me, it is important to worry about this.

    Why are you using printf as well as cout in your code? I would prefer sticking to one of these if I am writing any code and which one - > has been repeatedly discussed over here about the usage benefits of cout over printf. You could search for the thread using the Advance search link above. Paul's answer explains it all precisely and again (if I am not wrong). I ran your code on VC++ 6.0 and the output that I got is different from that of yours and this proves well the undefined behaviour:
    Quote:
    A::A () executing: this = 0x0012FF70
    A :: operator++ () executing: this = 0x0012FF70
    main: object = 0x1
    End of main
    A::~A () executing: this = 0x0012FF70
    Regarding the correctness of this code...
    This was simply an experiment, see my comments above to Paul.
    Regarding the printf...
    Yes, I agree you should only use one and not the other, and yes I prefer to use cout over printf. However, there have been times, when using posix threads, that cout would lock up on me and never return (and this has even been while wrapping the cout's with flock/funlock), and so in those cases I have resorted to printf (which seemed to alleviate my problem). Anyway, this is beside the point. See my previous respose to Paul above as to why I was using the printf function (this was just an experiment which brought up a concern as explained above). As far as the unexplained behavior regarding the return from printf, again, I didn't really care about that (and yes, I've even seen a return type similar to what you showed in your running of this code). My whole concern is related to the creation of an object for which there is no corrsponding destruction (see comments above). Interesting that your output does not show the creation of the second object!?
    Quote Originally Posted by exterminator
    I am not at all sure what you were trying to prove or see with the given piece of code? One objective is proved wrong above..If you want to see the creation of a copy and its deletion while passing arguments by value you could make your own function to verify that and not use printf. I assure you that it happens well and good and you may verify that. There would be no memory leaks or undeleted objects left at all that are created on the stack.
    Yes, I know construction/destruction works perfectly well, but not in this case, again, see my comments to Paul above. I was trying to use the simplest example that I could come up with, and hopefully get a reasonable response as to what may be going on. Again, interesting that your output does not show the second creation of the object.
    Quote Originally Posted by exterminator
    Some inputs from my side (although this doesnt directly relate to your actual doubt but still you might find it interesting) : While overloading the '++' operator you need to be careful enough to provide both the overloads preincrement as well as postincrement because your class user might use it as in either ways. And when he/she does that you class isnt full proof to support it. That would be wrong behavior since your overloads should behave naturally as they usually/generally do. A sample implementation can be seen at this link...
    Yes, I'm aware that you should overload both prefix and postfix versions of those operators. I was not trying to show a complete definition of some class. The prefix increment operator just so happens to be the function I chose to make my point with the call to the printf function (and I was trying to show it with as little code as possible). Again, my whole concern was the lack of destruction of the created object (see comments to Paul above).

  5. #5
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Destruction???

    Compilers are free to optimize away copy construction.

    But why couldn't you have created a dummy function and test that? Why did you choose "printf()" to experiment with? By choosing printf(), your results can be flawed because of the undefined behavior.
    Nevertheless, memory is still being used that never appears to be getting released.
    Maybe this is a result of the (I'll say it again) undefined behavior. Undefined behavior means just that -- anything can happen -- destructors not being invoked, code crashing, or code working perfectly fine. You don't know.

    Post code that doesn't invoke undefined behavior, then show us your results.

    Regards,

    Paul McKenzie
    Last edited by Paul McKenzie; August 13th, 2005 at 01:57 PM.

  6. #6
    Join Date
    Feb 2005
    Location
    "The Capital"
    Posts
    5,306

    Thumbs up Re: Destruction???

    Quote Originally Posted by sszd
    So I began thinking to myself, "gee, is this some sort of memory leak?", even though I believe it's on the stack as opposed to the heap. Nevertheless, memory is still being used that never appears to be getting released.
    This is what happens when you have an undefined behaviour. The code is not guranteed to show any standardized correct behaviour. It could become a villian in your whole program when such thing happens. Villian who eats up memory, causes abnormal behaviour...causes abnormal termination even. It could cause anything. No assurance...no predefined behaviour...nothing. That is what is meant by undefined.
    Quote Originally Posted by sszd
    Of course, if this were in a block of code as opposed to the program just exiting it would be even more of a concern (although I've heard of platforms that do not release memory even after a program ends).
    There may be such programs. But you would never want to make a similar one. Believe me. By the way, what program was that? Are you talking about the garbage collection and related stuff? Well, that is a different story altogether. No links of it are there with your story.
    Quote Originally Posted by sszd
    Yes, I agree you should only use one and not the other, and yes I prefer to use cout over printf. However, there have been times, when using posix threads, that cout would lock up on me and never return (and this has even been while wrapping the cout's with flock/funlock), and so in those cases I have resorted to printf (which seemed to alleviate my problem).
    Well, I am not quite familiar with posix thread and the issue that you might have faced but yes you could have had posted another thread with this query as to how to achieve what you wanted using the C++ way. That would have helped me get to know something and you of course if there had been a C++ way of achieving your aim. But well, if you elaborate I would try getting down to this and who knows I might come up with a C++ solution to it but yes this should be in a different thread (if you wish to put it).
    Quote Originally Posted by sszd
    As far as the unexplained behavior regarding the return from printf, again, I didn't really care about that (and yes, I've even seen a return type similar to what you showed in your running of this code).
    And that is why you cannot just not really care about the undefined behaviour.
    Quote Originally Posted by sszd
    My whole concern is related to the creation of an object for which there is no corrsponding destruction (see comments above).
    Why did you not try making your own Variadic function and see what are the results. Why did you have to chose some function like printf for which the behavior is defined to be undefined.
    Quote Originally Posted by sszd
    Interesting that your output does not show the creation of the second object!?[/size]I was trying to use the simplest example that I could come up with, and hopefully get a reasonable response as to what may be going on. Again, interesting that your output does not show the second creation of the object.
    Told ya, undefined behavior. Paul was very clear about it.
    Quote Originally Posted by sszd
    Yes, I'm aware that you should overload both prefix and postfix versions of those operators. I was not trying to show a complete definition of some class. The prefix increment operator just so happens to be the function I chose to make my point with the call to the printf function (and I was trying to show it with as little code as possible).
    I had no way of knowing that you did know about it. Apologies for my wrong assumption but well it was an assumption and for me to not go the wrong way you should have had clarified. Also. interestingly - to do whatever you have done with as little code as possible - you would not have had, in any case, needed to have the overloaded ++ operator...you know. A plain simple object passed to printf would have made you successful to get results for your experiment. I hope I did make some sense. Cheers.
    Last edited by exterminator; August 13th, 2005 at 02:29 PM. Reason: some grammatical errors :)

  7. #7
    Join Date
    Feb 2005
    Location
    Normandy in France
    Posts
    4,590

    Re: Destruction???

    Even if a behavior is undefined, we can explain a result that may appear on one version of one compiler, knowing that with another compiler or with another version of the same, the behavior may be very different. And the explanation may be very long in some cases (needing to know the whole source code of the compiler in extreme cases...).

    But i think that it is not too hard to explain the behavior in the case of sszd's code.
    You probably know that with __cdecl calling convention the caller cleans up the stack, while with __stdcall, the callee cleans up the stack.
    But, in both calling conventions (with BC++ and maybe VC++ and other compilers, but i suppose that it is not really standardized), with non-POD types, the callee calls destructors of arguments.
    So, since a function does not know the number, nor the types of variadic arguments passed to it, it cannot destroy the objects passed as variadic arguments (if the function has an external linkage). The best it can do, is hoping that all variadic arguments are POD types.
    On this side, the behavior may not be variable (assuming that the __cdecl calling convention is compatible between compilers).

    On the side of the caller, the behavior may be variable, and i can see four main compiler possible attitudes when passing non-POD types as variadic arguments:
    1. Emit a compilation error: It is easy for the compiler to detect this type of error, but i suppose that no, or few compilers have this behavior.
    2. Copy-construct objects independently of the type of argument (variadic or non-variadic).
      This is probably the behavior of VC++, what explains that the object is copy-constructed, but not destroyed (since the caller don't matter of destroying any argument, and the callee don't know that there may be non-POD types passed).
    3. Copy-construct non-variadic arguments and just do a raw copy of variadic arguments.
      This behavior is maybe less dangerous than the previous one, but do still very bad things.
      And for sure, no programmer should rely on it, and no programmer will do, because probably no compiler has such behavior.
      And i wonder why a compiler could produces such behavior, since, if the compiler-implementors were producing special code for variadic arguments, instead of choosing behavior 3, they would use behavior 1.
    4. Do many other weird things.
      Including linker errors, invalid executable, process crashes (the more probrable), formatting concensiously the hard drive on real-mode platforms, outputing an error message every second saying the programmer has done bad things...
      The list is not limitative...



    Maybe my explanation is totally erronous, and it is just luck if i explained the behavior. Maybe changing one line of code could change totally the behavior.
    It is just a "probable" explanation.

  8. #8
    Join Date
    Feb 2005
    Location
    "The Capital"
    Posts
    5,306

    Thumbs up Re: Destruction???

    Quote Originally Posted by SuperKoko
    Copy-construct objects independently of the type of argument (variadic or non-variadic). This is probably the behavior of VC++, what explains that the object is copy-constructed, but not destroyed (since the caller don't matter of destroying any argument, and the callee don't know that there may be non-POD types passed).
    I dont know what was used by sszd but I used VC++ 6.0 and this is not what happens with it !! By the way, nice try to explain things.

  9. #9
    Join Date
    Feb 2005
    Location
    Denver
    Posts
    353

    Re: Destruction???

    Latest update...

    Since there seemed to be so much concern using the printf and operator++ functions, I've reworked this experiment. Hopefully the following will appease those concerned...
    Code:
     
    #include <iostream> #include <iomanip> using std::cout; using std::endl; using std::hex; class A {
    public:
    A () { cout << “\n A::A () executing: this = 0x” << this << endl; } A (const A&) { cout << “\n A::A (const A&) executing: this = 0x” << this << endl; } ~A () { cout << “\n A::~A () executing: this = 0x” << this << endl; } A& fn () { cout << “\n A::fn () executing: this = 0x” << this << endl; return *this; }
    }; void fn (...) {
    cout << "\n fn (...) executing" << endl; va_list ap; va_start (ap, void); A& a = va_arg (ap, A); cout << "\n fn (...): &a = 0x" << &a << endl; va_end (ap);
    } int main () {
    cout << hex; A a; fn (a.fn ()); cout << “\n\n End of main” << endl;
    }

    Execution of program...

    A::A () executing: this = 0xffbee4db

    A::fn () executing: this 0 0xffbee4db

    A::A (const A&) executing: this = 0xffbee4da

    fn (...) executing

    fn (...): &a = 0xffbee4da

    End of main

    A::~A () executing: this = 0xffbee4db

    End of execution!

    The results are virtually identically to the original. So again, why is the 2nd creation of A never destroyed??? In case this may help, the following is the preprocessor output of void fn (...).
    Code:
    void fn (...)
    {
    Code:
    cout << "\n fn (...) executing" << endl; va_list ap ; ( void ) ( ap = ( __va_list ) & __builtin_va_alist ) ; A & a = ( ( A * ) __builtin_va_arg_incr ( ( A * ) ap ) ) [ 0 ] ; cout << "\n fn (...): &a = 0x" << & a << endl ; ( void ) 0 ;
    }

    Earlier in the preprocessor output it shows va_list typdef'd as __va_list, and __va_list is typdef'd as void *. The __builtin_... functions are internal and so I cannot see how they are defined. However, the creation of the 2nd object (via the copy ctor) occurs prior to the execution of the fn (...) function. In fact, the code within this function is completely irrelevant. Comment out the code and the results are still the same (no destruction of the 2nd object). So here again, the compiler felt a need to create a new object, yet it felt no need to destroy it?!

    By the way, this was not compiled using VC++ (someone had a question about that). This was compiled using Sun WorkShop 6 update 2 C++ 5.3.


  10. #10
    Join Date
    Feb 2005
    Location
    "The Capital"
    Posts
    5,306

    Thumbs up Re: Destruction???

    First of all, I appreciate your attitude. Nice. Secondly, there is an issue with your code. The variadic function that you have written is not ISO C standard compliant. It says that you should have atleast one fixed argument before the "...". I will quote:
    A function that accepts a variable number of arguments must be declared with a prototype that says so. You write the fixed arguments as usual, and then tack on `...' to indicate the possibility of additional arguments. The syntax of ISO C requires at least one fixed argument before the `...'.
    AND
    Portability note: For some C compilers, the last required argument must not be declared register in the function definition. Furthermore, this argument's type must be self-promoting: that is, the default promotions must not change its type. This rules out array and function types, as well as float, char (whether signed or not) and short int (whether signed or not). This is actually an ISO C requirement.
    The link to this information is - www.gnu.org.

    Well, I fixed that issue by making an int argument as a fixed one and tried running the code on VC++ 6.0 (Well, I am sorry but this is what I have access to right now). The code:
    Code:
    #include <stdarg.h>
    void fn (int count, ...){
    	cout << "\n fn (...) executing" << endl;
    	va_list ap;
    	va_start(ap, count);
    	A& a = va_arg (ap, A);
    	cout << "\n fn (...): &a = 0x" << &a << endl;
    	va_end (ap);
    }
    int main (){
    	cout << hex;
    	A a; fn (1, a.fn ());
    	cout << "\n\n End of main" << endl;
    	return 0;
    }
    And the output that I got: (removed extra lines)
    A::A () executing: this = 0x0012FF70
    A::fn () executing: this = 0x0012FF70
    fn (...) executing
    fn (...): &a = 0x0012FF1C
    End of main
    A::~A () executing: this = 0x0012FF70
    Press any key to continue
    And the copy constructor here is not being called this time either on VC++ 6.0 as was the case earlier with me for the undefined scenario. Now this makes me think about the second part of the quote that I have provided above. Is class A a self promoting type? If not then again this would result in an undefined behaviour as it seems to be a requirement by the ISO C. But C doesnt deal with classes? And this raises doubts in my mind if using classes/objects are fine with variadic functions? Or are there any specific guidelines for variadic functions in C++? I would wait for someone to clarify because I lack the knowledge in this case and then lets see how can we explain this behaviour.
    Last edited by exterminator; August 16th, 2005 at 02:46 PM. Reason: removed the class code - same as above

  11. #11
    Join Date
    Feb 2005
    Location
    Denver
    Posts
    353

    Re: Destruction???

    I apologize if you feel there was an "attitude" with my previous post. I was simply expressing the concern others had regarding some of the functions that existed in the 1st version of this code. I did not mean to offend anyone. Sorry.

    Thank you for your input. I'm glad to see this is not just a problem with my compiler. Maybe your right, maybe this is an issue with variadic functions!? Just as a side note though... I was looking at this as if the caller were creating the object based on the parameter necessary for the function call. If the callee were creating the object then I can see possibly how there would be no way the object could be destroyed (due to the fact that it really doesn't know what kind of object it is dealing with). However, if the caller is really the one creating the object, then I can see no reason the object wouldn't get destroyed after the program ends (regardless of the fact that we are dealing with a variadic function).

  12. #12
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Destruction???

    From the C++ ANSI standard, 18.7.3
    The parameter parmN is the identifier of the rightmost parameter in the variable parameter list of the function definition (the one just before the ...).
    If the parameter parmN is declared with a function, array, or reference type, or with a type that is not compatible with the type that results when passing an argument for which there is no parameter, the behavior is undefined.
    Regards,

    Paul McKenzie

  13. #13
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Destruction???

    Quote Originally Posted by exterminator
    And this raises doubts in my mind if using classes/objects are fine with variadic functions?
    The problem is that the syntax for passing an object by reference or by value is the same. This I believe is where the ambiguity (and undefined behavior) can occur.
    Code:
    void fn (int count, ...)
    {
    }
    
    int main ()
    {
    	cout << hex;
    	A a; 
            fn (1, a.fn ());  // are you passing the return value of a.fn() by
                                   // value, or by reference?  There is no way to know.
    }
    The compiler may assume that the "..." means pass by value, or it could assume pass by reference for a C++ variadic function. The compiler will generate the appropriate code (copy constructor for by-value, no copy constructor if by-reference).

    Regards,

    Paul McKenzie

  14. #14
    Join Date
    Feb 2005
    Location
    "The Capital"
    Posts
    5,306

    Thumbs up Re: Destruction???

    Quote Originally Posted by sszd
    I apologize if you feel there was an "attitude" with my previous post. I was simply expressing the concern others had regarding some of the functions that existed in the 1st version of this code. I did not mean to offend anyone. Sorry.
    I appreciated your attitude and you are apologizing for it? You should have attitude, it's not a bad thing until it gets uncontrolled and your case deserves appreciation because you are working hard to get things clarified.

    Thanks Paul. That was a very nice explanation and it surely justifies the undefined behaviour in the above case. But the issue of pass by value and pass by reference ambiguity (leading to undefined behaviour) means that we would not be able to use variadic functions in C++ on the whole. They are only meant for C where we do not have anything like pass by reference. Is this conclusion correct?
    Last edited by exterminator; August 17th, 2005 at 01:33 AM.

  15. #15
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Destruction???

    Quote Originally Posted by exterminator
    Thanks Paul. That was a very nice explanation and it surely justifies the undefined behaviour in the above case. But the issue of pass by value and pass by reference ambiguity (leading to undefined behaviour) means that we would not be able to use variadic functions in C++ on the whole. They are only meant for C where we do not have anything like pass by reference. Is this conclusion correct?
    Well, here is the clause in the ANSI standard that should clear up things:

    From ANSI C++ 5.2.2.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 value of the argument by invoking va_arg (18.7).
    ...
    if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the program is ill-formed. If the argument has a non-POD class type, the behavior is undefined.
    So if it's printf() or your own home-made variadic function, the behavior is undefined if you pass a non-POD object as one of the "..." arguments.

    Regards,

    Paul McKenzie

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured