Thread: When to handle memory explicitly?

  1. #1
    Registered User
    Join Date
    Jan 2010
    Posts
    19

    When to handle memory explicitly?

    Hello, I'm new to these fora.

    I used to "play around" with C and C++ 10 years ago, and I have been coding different languages a little bit also, like asm (embedded systems), PHP, e.t.c.

    Something I've never cared about is memory handling. I've now learned how to use the "new" and "delete" operators, but what I haven't been able to find out is basicly when to use them. To illustrate what I mean, i have two examples (regard the code as pseudo code, I've intentionally simplified it for easier reading);

    Code:
    Class MyClass
    {
       Public:
            MyClass(void);
       Private:
       int * MyVariable;
    };
    
    MyClass::Myclass(void)
    {
        MyVariable = new int;
        *MyVariable = 0;
    }
    
    int main(args..)
    {
       MyClass MyInstance;
    }
    Code:
    Class MyClass
    {
       Public:
            MyClass(void);
       Private:
       int MyVariable;
    };
    
    MyClass::Myclass(void)
    {
        MyVariable = 0;
    }
    
    int main(args..)
    {
       MyClass * MyInstance;
       MyInstance = new MyClass;
    }
    A third option would be like the last example, but using only "MyClass MyInstance;" to declare and initialize and instance of MyClass.

    Now, when should I use the different ways of implementing the memory handling, and when should I just leave it all to the compiler/linker? Is there any difference in example 1 and 2 in regards of efficiency, since a class might have several member variables, so the "new" operator will have to be called multiple times for each instance of the class, whereas in the second example the "new" operator is only called once for each instance of the class? Is that thinking correct?

    I am thankful for any input on this topic!

  2. #2
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    You use new whenever you need an object you can't use a local or member variable for (depending on context).
    You use delete when you implement something that has to manage memory, i.e. a data structure or a smart pointer. You try to avoid delete at all other times by using such managers instead. If you use a few good libraries of interesting data structures, e.g. the standard containers, Boost's smart pointers, various other containers in Boost, then you will very rarely have to use delete.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  3. #3
    Registered User
    Join Date
    Jan 2010
    Posts
    19
    Oh, so for example when I need to create an object outside the scope of the function, i use new and then pass the pointer on, and then that object will live after the function exits, but if I create the same object using a local variable, that object would be lost when the function exits?

  4. #4
    Registered User C_ntua's Avatar
    Join Date
    Jun 2008
    Posts
    1,853
    If you do the second you will create a memory leak. Which is bad and you should simply never do it.
    Think in the way things actually happen. Lets say you have this
    Code:
    void foo()
    {
        int* my = new int;
    }
    when foo is called a local variable "my" will be allocated. "my" has a type of a "int*".
    So you will have when "int* my" is called
    Code:
    LOCAL FOO
    address   value
    my            some random value
    when the " = new int" is called you will have
    Code:
    LOCAL FOO
    address   value
    my            0x123
    
    HEAP
    address   value
    0x123       some random value
    that said, when "foo" returns the local variable "my" is deallocated. So you have a "some random value" in the address 0x123, but that address cannot be accessed because the pointer is de-allocated. Memory leak.

    If you modify foo you could do
    Code:
    int* foo()
    {
       int* my = new int;
       return my;
    }
    now, with the above numbers, foo() would return the value 0x123. "my" will still be de-allocated, but you can assign the value 0x123 to another variable. Thus saving the memory address of the memory allocated by "new int".

    Remember, the golden rule.
    -For each new you need a delete

    Thus, whatever you allocate dynamically, you have to de-allocate yourself. The same with malloc()/free() in C.

    C++ offers destructors. You should study them a bit because they are the main mechanism for automatic memory management. As mentioned, the standard containers, like vector<> de-allocate memory automatically with the destructor so you won't have to worry about it.

    For your initial example. Lets say
    Code:
    class A
    {
    public:
      int* a;
      ...
      int* z;
      A() {
         a = new int;
         ...
       }
    };
    
    int main()
    {
       A myA;
    }
    than this
    Code:
    class B
    {
    public:
       int a;
       ...
       int z;
    ...
    };
    
    int main()
    {
        B* myB = new B;
    }
    A will call a lot more times new. Which might be slower and less efficient than calling new once for B. But the result is not the same. Object B will entirely be allocated on the heap, pointed by "myB". Object A will be allocated in local space of main(). So the object pointer and all its member variables (int* a....int* z) will be in the local space of main(). All the objects those member variables point to will be allocated on the heap. So you will need to call delete that many times.

    You might think that B is better. But I would say that most of the time it is not. Cause, you can use a combination of the two and do something like:
    Code:
    class A
    {
    public:
         B* myB;
         A() {
             myB = new B;
         }
    }
    
    int main()
    {
         A myA;
    }
    The benefit? You call new once again. But now you can use a destructor to do the de-allocation automatically. You would simply add
    Code:
    class A
    {
    public:
         B* myB;
         A() {
             myB = new B;
         }
         ~A() {
              delete myB;
         }
    }
    What happens is that when "myA" is de-allocated automatically when main() returns, the destructor is automatically called. The destructor will de-allocate the internal object B. Which needs to be manually de-allocated since it was allocated with new. Thus, with this chain reaction everything is deleted. Thus, using destructors is most of the times the "clever" solution.

  5. #5
    Registered User
    Join Date
    Jan 2010
    Posts
    19
    C_ntua:

    I am sorry that I didn't clarify that I know about destructors. I simply left them out of my example because I figured they were out of the scope of my question.

    In my first example i would naturally place "delete" operators for each of the member variables in the destructor of the class, and in the second example I would delete the object with "delete" once I don't need it anymore.

    That said, I did learn a lot from your reply, especially the last part, so thank you!

  6. #6
    Registered User C_ntua's Avatar
    Join Date
    Jun 2008
    Posts
    1,853
    Well, I guess you would have known, using it for 10yrs. But you didn't specify so assumed the worst case scenario

    As a final note about new and its efficiencty, it depends. Calling it a lot of times might lower efficiency for two reasons
    1) You will end up in a non-consistent memory block. Memory consistency offers room for optimizations. For example, if you would do this
    Code:
    obj.a = 1;
    obj.b = 2;
    ....
    then the hardware is most likely to have obj.b...ojb.z variables in cache memory when obj.a = 1 is called. On the other hand if you do this
    Code:
    obj->a = 1;
    obj->b = 2;
    ....
    then all the pointers can be in cache, but the memory addresses the point to not. Of course it could happen that they are, but it is less likely.

    Just an example here. What exactly happens again depends.

    2) Each new requires some time to be executed.


    Those two been said, from the two examples you gave in the beginning, I would choose the second. With one new. And wrap it around another class with a destructor to delete it if you want an automatic memory management.
    And always remember that in very low memory systems, being able to delete objects yourself might be important. Because you might not be able to allocate the whole object all at once, so you would have to allocate and deallocate manually.

  7. #7
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Another note is that usually new is optimized for large allocations, so calling it a lot of times for small allocations causes overhead.
    For a lot of small allocations, it's better to avoid new or to use a memory pool.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  8. #8
    Registered User
    Join Date
    Jan 2010
    Posts
    19
    Thank you C_ntua and Elysia, I think I got it now!

  9. #9
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    New and delete also require a 'pop' if you will into kernel mode to actually do or undo the allocation. Dynamic memory allocation should be done up front and should be avoided after this. This probably isn't as important in PC desktop dev as it is in embedded dev where you do not have a swap file. Pre-allocate all your objects and use them later or you can go with a system like Elysia suggests where you have pre-allocated fixed size memory pools you 'allocate' from but in reality have already been allocated. This allows you to know the exact size and memory footprint of your application and it is faster. However realize with memory pools there will probably be portions of the pools that are not used. For example if you use a memory pool size of 16kb and need 32kb then you will use 2 pools. If you need 35 kb then you will need 3 pools but the last pool will for the most part be empty. Pool size determination varies from application to application. You can also create a stack-type memory allocator which allocates 1 huge chunk of memory from the heap and then hands the memory out exactly like a stack. On allocation the memory pointer is moved up and on de-allocation the memory pointer is moved down. This is very fast but not suitable for all systems since order of allocation/de-allocation is extremely important. Most systems will mix these two approaches.

  10. #10
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    New and delete also require a 'pop' if you will into kernel mode to actually do or undo the allocation.
    No, on all desktop systems, the allocators will only switch to kernel mode if they have exhausted the pages they allocated so far. A memory allocator will always grab one or more pages at once from the OS and then use its own data structures to subdivide it.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  11. #11
    Registered User
    Join Date
    Oct 2008
    Posts
    61
    one question why in data structures c++ like those that use struct and contain nodes that links pointers and addresses

    no delete is required.aren't they also using pointers as well..
    sorry for bumping this thread

  12. #12
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Quote Originally Posted by MyRedz View Post
    one question why in data structures c++ like those that use struct and contain nodes that links pointers and addresses

    no delete is required.aren't they also using pointers as well..
    sorry for bumping this thread
    The mere use of a pointer doesn't necessarily imply management of the memory at that address. You might have a fixed-size array on the stack that contains pointers to the elements of another fixed-size array of nodes, for example. In that situation, obviously, you wouldn't even fuss with new/delete.

    If you do need to manage memory, though, just remember to always use RAII (eg: smart pointers) rather than resort to "manual" book-keeping, which is error-prone and, anyway, a needless duplication of effort.

  13. #13
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by MyRedz View Post
    one question why in data structures c++ like those that use struct and contain nodes that links pointers and addresses no delete is required.
    If you're talking about node-based containers, e.g. linked lists, those actually need to use delete, or something equivalent. But they hide it from the user of the container. That's what containers are for.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  14. #14
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by CornedBee View Post
    No, on all desktop systems, the allocators will only switch to kernel mode if they have exhausted the pages they allocated so far. A memory allocator will always grab one or more pages at once from the OS and then use its own data structures to subdivide it.
    Not true on Mac OS X. On that platform, every single malloc() (new calls it as well) involves an RPC call to another process. I wish it wasn't true.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  15. #15
    Registered User jeffcobb's Avatar
    Join Date
    Dec 2009
    Location
    Henderson, NV
    Posts
    875
    I would also like to throw in the scenario where you are working in an embedded environment ie one that has a limited stack space. In such a scenario not only is recursion dangerous but over-reliance on automatic variables a nuisance as well.
    C/C++ Environment: GNU CC/Emacs
    Make system: CMake
    Debuggers: Valgrind/GDB

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Mutex and Shared Memory Segment Questions.
    By MadDog in forum Linux Programming
    Replies: 14
    Last Post: 06-20-2010, 04:04 AM
  2. Copy HWND into shared memory buffer
    By Opariti in forum Windows Programming
    Replies: 2
    Last Post: 12-26-2009, 01:08 PM
  3. Relate memory allocation in struct->variable
    By Niara in forum C Programming
    Replies: 4
    Last Post: 03-23-2007, 03:06 PM
  4. pointers
    By InvariantLoop in forum C Programming
    Replies: 13
    Last Post: 02-04-2005, 09:32 AM
  5. multiple indirection...
    By doubleanti in forum C++ Programming
    Replies: 7
    Last Post: 08-31-2001, 10:56 AM