Thread: Avoiding leaks on dynamically created data members

  1. #1
    Registered User Mario's Avatar
    Join Date
    May 2002
    Posts
    317

    Avoiding leaks on dynamically created data members

    I have the following class, Room, where i'm storing info for each room in the .... you guessed it... RPG world. The program we never end.
    I've stripped the class code for you of about everything except the part that i'm trying to work out...
    Code:
    class Room{
       private:
          char* pDescription;  //Note i'm using a pointer here. This is were my question starts
          static int tally;  //I added this one in the hope it helps solving my problem
       public:
          ~Room(){ delete [] pDescription; tally--; }
          Room(const char* desc="Room Description -empty-")
          {
             if(!(pDescription = new char[strlen(desc)+1])) 
                { cout <<"Out of memory!"; exit(1); } //This is my problem. The exit(1) there.
             strcpy(pDescription, desc);
             tally++;
          }
    
          static int Count(){ return tally; }
          void Description(){ cout << endl << pDescription; }
    };
    int Room::tally = 0;
    According to what I have there, if the constructor can't allocate memory for the char array, it aborts execution. This is fine and dandy. But if I have other objects of this class in the heap already when this happens, their destructors will never get called, and I will just have this huge memory leak... considering each description data member can be up to 500 characters.

    How can I avoid this leak? How should I build that constructor check so that it aborts execution after and only after deallocating all objects created before.

    Please note, I need that pDescription there. You could argue I could simply create a [501] array and leave with the wasted space while avoiding having to deal with a dynamically created data member. That is true. But this code belongs to the RPG world builder component and not to the playing one. That is, there's the need of having a supposedly huge array of Room objects open at all times. Hence the need to optimize that particular data member.
    Last edited by Mario; 05-30-2002 at 10:39 AM.
    Regards,
    Mario Figueiredo
    Using Borland C++ Builder 5

    Read the Tao of Programming
    This advise was brought to you by the Comitee for a Service Packless World

  2. #2
    Registered User
    Join Date
    Apr 2002
    Posts
    95
    cant you call the destructor somehow just before exit(1)

  3. #3
    Registered User Mario's Avatar
    Join Date
    May 2002
    Posts
    317
    I believe I could.
    That's part of the reason why I started implementing the count function member. So that I would know how many objects I have in existence. But I have no idea on how to call object names at runtime.
    Regards,
    Mario Figueiredo
    Using Borland C++ Builder 5

    Read the Tao of Programming
    This advise was brought to you by the Comitee for a Service Packless World

  4. #4
    Registered User Mario's Avatar
    Join Date
    May 2002
    Posts
    317
    Ok.. I've been thinking on this. And I really can't see a way to avoid leaking with this code.

    I've been reading an interesting article at programmer's heaven regarding smart pointers and how to implement them. One of the most important attributes of this coding technique is that it's exception safe... exactly what i was looking for. But to be honest, I lack the knowledge (for the time being, I must add ) to implement it on my class.

    For those of you who may be interested, you can find it here

    As for me... i'll lower my weapons and resign myself with a [501] sized array.
    Regards,
    Mario Figueiredo
    Using Borland C++ Builder 5

    Read the Tao of Programming
    This advise was brought to you by the Comitee for a Service Packless World

  5. #5
    Registered User
    Join Date
    Apr 2002
    Posts
    95
    I wonder if exit() doesn't call the appropriate destructor the same as return would. I will have to expermint with this by a "verbose" destructor.

    To call an object function just use

    objectname.function()

    Just a newbie but I would guess calling the destructor explicitly would be done like this
    objectname.~destructor()
    or maybe objectname.destructor

    Maybe I'm off base a little if your using a pointer to the object it'-
    objectname->function()

  6. #6
    Registered User Mario's Avatar
    Join Date
    May 2002
    Posts
    317
    I've been searching everywhere for this info. It's almost ridiculous that such an important feature of a class, like enabling dynamic data members is so badly documented in terms of memory leaks. And on these groups nobody seems to know either...

    Right now i'm considering the possibility of throwing an exception on the constructor if the allocation fails (as per advise in programmersheaven forum). The only problem is that, I have to figure out a way of knowing at runtime what objects of the class Room exist and delete those.

    But how do I delete a non-dynamic object? I mean, I can use 'delete' for dynamic ones... but what about an object I declared as 'Room myRoom'. How do I force deletion of this object and thus force the destructor to do it's thing (see code above)?

    Also, How can I keep track of objects in existence, so that I know which objects to delete?... A static array of pointers to Room objects declared in the class itself and automatically filled by the class constructor? But, if so (and i'm not sure the constructor knows the object memory address), this array would itself need to be dynamic and thus complicate my life even further... Dunno. I'm stuck.
    Regards,
    Mario Figueiredo
    Using Borland C++ Builder 5

    Read the Tao of Programming
    This advise was brought to you by the Comitee for a Service Packless World

  7. #7
    Registered User
    Join Date
    Mar 2002
    Posts
    125
    Just a wild guess, but would it be impossible to just type ~Room(), to make sure the destructor is called? Or is are con- and destructors not functions at all?
    Also, maybe you can delete non-dynamic things by using
    delete &myroom;
    Also, exit(1); might already call all destructors. Why don't you create a program with a class that allocates 2000000 bytes of memory and de-allocates it in the destructor, call exit and see if your system-resources are messed?

    (EDIT) Well, some of my solutions don't work anyway. As a final try, why don't you make a normal class function that is perfectly similar to the class destructor, and make a function that calls it for all Room instances? (I hope they're kept track of...)
    Last edited by Boksha; 05-31-2002 at 09:54 AM.

  8. #8
    Registered User Mario's Avatar
    Join Date
    May 2002
    Posts
    317
    Yeah... None of the options would work that way Boksha. I tried that before. But thanks for taking interest

    I finally got Stroustrup's book (3rd edition). I'll find my answer there. In the meantime I'll make a change to that class:

    1 - I'll no longer use a dynamic char array. But instead I'll use the STL and declare that data member as a string. Although it may not solve my problem directly, at least the memory management responsibility jumps to that container class. This way I can blame other than myself

    Taken from Stroustrup's book (1.2 - page 6):
    The most important thing to do when learning C++ is to focus on concepts and not get lost in language-technical details. The purpose of learning a programming language is to become a better programmer. [...] For this, an appreciation of programming and design techniques is far more important than an understanding of details; that understanding comes with time and practice.
    My apologies to Mr. Stroustrup if I incurred in any typo. But this simple paragraph opened my eyes.
    Regards,
    Mario Figueiredo
    Using Borland C++ Builder 5

    Read the Tao of Programming
    This advise was brought to you by the Comitee for a Service Packless World

  9. #9
    S­énior Member
    Join Date
    Jan 2002
    Posts
    982
    >How do I force deletion of this object and thus force the destructor to do it's thing (see code above)?

    You let your program die naturally rather than forcing it to quit. If you're programming for an o/s that doesn't clean up when a process terminates, just throw an exception in your constructor. If your constructor does multiple allocations you're going have to test each allocation and if one fails delete the previous successful internal allocations before throwing, as the destructor won't be called for this instance.

    When you throw you wont be forcing an application to quit, allowing destructors to be called for objects created on the stack, and giving you an opportunity to delete any previously allocated heap objects in your catch handler. Something like -

    Code:
    #include <iostream> 
    
    using namespace std; 
    
    char* names []= {"stack's", "heap's"};
    
    class only_two
    {
    	static int b;
    	char* c;
    	
    public:
    	only_two(){
    		c=names[b++];
        	if(b>2)throw 1;
    	}
    	
    	~only_two(){
    		cout << c << " destructor\n";
    	}
    };
    
    int only_two::b=0;
    
    int main() 
    {
    	only_two stack;
    	only_two* heap;
    	try
    	{
    		heap = new only_two;
    		only_two bad;
    	}
    	catch (int)
    	{
    		cout << "before heap's destructor\n";
    		if(heap)
    			delete heap;
    		cout << "before stack's destructor\n";
    	}
    	
    
    	
    	return 0;
    
    }
    You should probably put both the objects in the try block, in which case the 'stack' object would be destructed at the end of the try block; before the catch handler. Also a modern o/s will clean up after you anyway so you don't have to worry about it.

    >Also, How can I keep track of objects in existence, so that I know which objects to delete?...<

    It's necessary to maintain pointers to dynamically created objects regardless of how you plan to end a program otherwise you're going to get memory leaks. If you're allocating stuff all over the place, it'd probably be an idea to use some kind of container to keep track of them.

    >A static array of pointers to Room objects declared in the class itself and automatically filled by the class constructor?<

    If you're allocating with new, it's returning a pointer to the allocated instance so there's no need to have the constructor register it's existence. If it's allocated on the stack then as long as your program ends properly then the destructor will be called automagically.

  10. #10
    Registered User
    Join Date
    Dec 2001
    Posts
    194
    I have 2 simply theories you could use to continue using your original code,
    1)Have the constructor pass back a result to the calling function

    Room(int & result, const char * desc = "room description");

    then call it like this from main()

    int result;
    Room Kitchen(result,"The kitchen");
    if ( result == 1 )
    {
    cout << "error with making the kitchen";
    return 1;
    }


    option 2)
    Have a global variable called "memAllocationFailed" and set it to false at the beginning.
    In the constructor, it the call to new fails, then set memAllocationFailed to true.
    Your main function, or setup function that was creating the rooms should check memAllocationFailed after each room, to see if it sould return control to main, and in turn the OS.

    I know global vars are bad practice for the most part, but this is a game so its ok

  11. #11
    Registered User
    Join Date
    Sep 2001
    Location
    Fiji
    Posts
    212
    You could create an Abstract base class, with a virtual destructor, and inherit from this class. I can't remember exactly how to implement this though. Or, perhaps there is an even easier way.

    kwigibo

  12. #12
    Registered User Mario's Avatar
    Join Date
    May 2002
    Posts
    317
    RpiMatty wrote:
    1)Have the constructor pass back a result to the calling function
    [...]
    2 Have a global variable called "memAllocationFailed" and set it to false at the beginning.
    In the constructor, it the call to new fails, then set memAllocationFailed to true.
    It's hardly my habit to allow class implementation to be dealt outside it's declaration. The simple fact I have to declare an int outside the class in order to be able to use it sounds to me almost like sacrilege.

    I liked your idea
    But with a slight change.... see below.

    As for option 2, like you so rightfully say global vars are bad practice (given or taken the unusual situation were you actually may need one). I'm new to C++, but unless this programming language proves otherwise, I'm yet to know of a situation were a global variable can't be replaced by some other coding technique. Mind you, I'm not saying I disagree with their use. I'm just stating that either their serve a very specific purpose or if they have a better alternative, better to use the alternative.

    On this particular case, I would create a private data member with the necessary public member function(s). It would avoid the global declaration and, at the same time, encapsulate all of the class implementation inside it's declaration.
    Regards,
    Mario Figueiredo
    Using Borland C++ Builder 5

    Read the Tao of Programming
    This advise was brought to you by the Comitee for a Service Packless World

  13. #13
    Registered User Mario's Avatar
    Join Date
    May 2002
    Posts
    317
    Originally posted by Sorensen
    [B]>How do I force deletion of this object and thus force the destructor to do it's thing (see code above)?

    You let your program die naturally rather than forcing it to quit
    [...]
    Thanks a lot Sorensen!

    I understood your code perfectly and you replied to about all my doubts regarding this! Amazing

    Also, the way you placed the exception handling... I didn't know that was possible. I'm referring to the placement of the throw inside the constructor in relation to the try-catch in main(). But looking at it, it now looks clear. And that alone answered yet another doubt that was forming on my mind.

    Thanks to all of you who replied here. You people are great

    PS:
    Before you say anything.... yes! I have a copy constructor for that class. I learned you are in deep trouble if you implement a dynamic data member without a copy constructor
    Regards,
    Mario Figueiredo
    Using Borland C++ Builder 5

    Read the Tao of Programming
    This advise was brought to you by the Comitee for a Service Packless World

  14. #14
    Unregistered
    Guest

    Angry thanks again

    I just love the way everyone helped out. thanks again

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 48
    Last Post: 09-26-2008, 03:45 AM
  2. dynamically loading data?
    By elninio in forum C++ Programming
    Replies: 15
    Last Post: 07-28-2008, 12:46 AM
  3. Not able to access private data members
    By smitsky in forum C++ Programming
    Replies: 31
    Last Post: 05-09-2004, 07:06 PM
  4. Accessing private data members
    By maloy in forum C++ Programming
    Replies: 11
    Last Post: 10-04-2002, 02:48 PM
  5. static and non-static data members
    By Unregistered in forum C++ Programming
    Replies: 2
    Last Post: 06-16-2002, 10:06 AM