Should one be worried about the stack & heap?

As a hobby programmer that is still learning C++ (I doubt you ever stop learning it), and only writes small applications for educational purposes and nothing production related, should I worry about what I'm allocating to the heap and what I'm leaving up to the stack?

I'm trying to adapt better practices and habits as I learn them and was curious about this one. What is the best practice when it comes to dealing with the heap and the stack and creation of class objects, variables, etc, etc?
Understanding the difference is handy, but non-essential. A fellow I've done some work with has been programming professionally for 15 years and didn't know the difference between the two a month or two ago. He's a VB programmer, so it's quite a bit different I suppose.

If you allocate memory, then free it. Pay attention to how many copies of your data are floating around in your program- you know the basics.

For myself, I try to use the stack whenever possible, but when programs require dynamism I generally move to the heap for those portions. Some boost/11/14 features like unique_ptr can make your life a lot easier when dealing with the more headache-y things.
@wizebin Very well. So in other words take note that it's there and be kind with releasing objects as needed, but don't stress too much about it?

Thank you for the input!
> should I worry about what I'm allocating to the heap and what I'm leaving up to the stack?
> I'm trying to adapt better practices and habits as I learn them and was curious about this one.

The basic rules for this are simple; learning good habits early is easy.
Unlearning bad programming habits after they have set in tends to be quite hard.

Adapted/extracted from CppCoreGuidelines https://github.com/isocpp/CppCoreGuidelines

1. Favour scoped objects.

A scoped object is a local object, a global object, or a member. ...
The members of a scoped object are themselves scoped and the scoped object's constructor and destructor manage the members' lifetimes.

1
2
3
4
5
6
7
8
9
10
11
12
void bad_function( int n )
{
    auto p = new Gadget{n};
    // ...
    delete p;
}

void good_function( int n) 
{
    Gadget g{n};
    // ...
}



2. Avoid calling new and delete explicitly

The pointer returned by new should belong to a resource handle (that can call delete). If the pointer returned by new is assigned to a plain/naked pointer, the object can be leaked.

In a large program, a naked delete (that is a delete in application code, rather than part of code devoted to resource management) is a likely bug.


3. Use std::unique_ptr or std::shared_ptr to represent ownership.
A raw pointer - a T* - is non-owning.

They can prevent resource leaks.


4. Favour the standard library over other libraries and "handcrafted code"

Code using a library can be much easier to write than code working directly with language features, much shorter, tend to be of a higher level of abstraction, and the library code is presumably already tested. The ISO C++ standard library is among the most widely known and well tested libraries. It is available as part of all C++ Implementations.


5. Favour std::array or std::vector over C-style arrays

C arrays are less safe, and have no advantages over std::array and std::vector.
For a fixed-length array, use std::array, which does not degenerate to a pointer when passed to a function and does know its size.
For a variable-length array, use std::vector, which additionally can change its size and handles memory allocation.


6. Favour std::string over dynamically allocated C-style strings

C-style strings are less safe, and have no advantages over std::string


7. Immediately give the result of an explicit resource allocation to a manager object

If you don't, an exception or a return may lead to a leak.

1
2
3
4
5
6
7
8
9
10
11
12
void bad_function( std::string path )
{
    auto file = std::fopen( path.c_str(), "r" );
    // ...
    std::fclose(file) ;
}

void good_function( std::string path )
{
    std::ifstream file(path) ;
    // ...
}
So in other words take note that it's there and be kind with releasing objects as needed, but don't stress too much about it?
No, it's absoutely crucial to know about it.

For instance: You must not return a pointer of the local variable from a function.
Using the standard library (string, vector, etc.) will help in this regard.


VB programs are a completely different thing.
You should keep in mind that the stack has a limited size. This is mainly a problem if you try to allocate large arrays on the stack or have too deep recursive calls.

A more important concept is storage duration.

Local function variables has automatic storage duration. This means they will automatically be destroyed when they go out of scope.

Things that has been dynamically allocated with new has dynamic storage duration. To destroy a dynamically allocated object you have to use delete (or a smart pointer that uses delete internally).

You should prefer automatic storage duration because it makes the code simpler and there is less risk of doing mistakes. Only use dynamic allocations if you have a good reason to do so.

Note that member variables of a class can be stored on the stack if the object was created as a local variable inside a function, but if the object was allocated on the heap the member variables will also be on the heap.

If you have a standard container (e.g. std::vector, std::string, ...) that is allocated on the stack only a very small piece of memory will actually be stored on the stack. The memory for all the elements in the container will be dynamically allocated. This means you usually don't have to worry about running out of stack space when allocating objects on the stack.
Last edited on
Topic archived. No new replies allowed.