Question

I am learning c++ and would like to know how a program like this is organized in primary-memory. I understand that there are a stack (with stackframes) and a heap. And I know that dynamically allocating something allocates it on the heap. This is done by operators like malloc or new. But I cant see them in this small c++ program.

The program consists of a main-class and a class named MyClass. This class has:

  • one constructor
  • one member variable (int)
  • one member-function

The main-class defines an object to Myclass and as well defines a pointer to this object.

SO - how are all this organized in memory?

#include <iostream>
using namespace std;

class MyClass {
    int i;
public:
    MyClass(int n) {
        i = n;
    }
    int get_nmbr() {
        return this->i;
    }
};


int main() {
    MyClass myClass(100), *p;
    cout << myClass.get_nmbr() << endl;
    p = &myClass;
    cout << p;
    return 0;
}
Was it helpful?

Solution

Let's go through this line by line.

int main() {

A new function starts with this line, and therewith a new scope.

    MyClass myClass(100), *p;

Two things happen here. One, a variable myClass is declared within the function's scope, which makes it a local variable and thus it's allocated on the stack. The compiler will emit machine instructions that reserve enough space on the stack (usually by bumping the sp stack pointer register), and then a call to the class constructor executes. The this pointer passed to the constructor is the base of the stack allocation.

The second variable p is just a local pointer, and the compiler (depending on optimizations) may store this value on the local stack or in a register.

   cout << myClass.get_nmbr() << endl;

Call the get_nmbr() method of the local myClass instance. Again, the this pointer points to the local stack frame allocation. This function finds the value of instance variable i and returns it to the caller. Note that, because the object is allocated on the stack frame, i lives on the stack frame as well.

   p = &myClass;

Store the address of the myClass instance in variable p. This is a stack address.

   cout << p;
   return 0;
}

Print out the local variable p and return.

All of your code is only concerned with stack allocations. The result of that is that when the function's scope is left/closed at execution time (e.g. the function returns), the object will be automatically "destructed" and its memory freed. If there are pointers like p that you return from that function, you're looking at a dangling pointer, i.e. a pointer that points at an object that's freed and destructed. (The behavior of a memory access through such a dangling pointer is "undefined" as per language standard.)

If you want to allocate an object on the heap, and therefore expand its lifetime beyond the scope wherein it's declared, then you use the new operator in C++. Under the hood, new calls malloc and then calls an appropriate constructor.

You could extend your above example to something like the following:

{
    MyClass stackObj(100);  // Allocate an instance of MyClass on the function's stack frame
    MyClass *heapObj = new MyClass(100); // Allocate an instance of MyClass from the process heap.

    printf("stack = %p heap = %p\n", stackObj, heapObj);

    // Scope closes, thus call the stackObj destructor, but no need to free stackObj memory as this is done automatically when the containing function returns.
    delete heapObj; // Call heapObj destructor and free the heap allocation.
}

Note: You may want to take a look at placement new, and perhaps auto pointers and shared pointers in this context.

OTHER TIPS

First things first. In C++ you should not use malloc.

In this program, all memory used is on the stack. Let's look at them one at a time:

MyClass myClass(100);

myClass is an automatic variable on the stack with a size equal to sizeof(MyClass);. This includes the member i.

MyClass *p;

p is an automatic variable on the stack which points to an instance of MyClass. Since it's not initialized it can point anywhere and should not be used. Its size is equal to sizeof(MyClass*); and it's probably (but not necessarily) placed on the stack immediately after myClass.

cout << myClass.get_nmbr() << endl;

MyClass::get_nmbr() returns a copy of the member i of the myClass instance. This copy is probably optimized away and therefore won't occupy any additional memory. If it did, it would be on the stack. The behavior of ostream& operator<< (int val); is implementation specific.

p = &myClass;

Here p is pointing to a valid MyClass instance, but since the instance already existed, nothing changes in the memory layout.

cout << p;

Outputs the address of myClass. Once again the behavior of operator<< is implementation specific.

This program (kind of) visualizes the layout of the automatic variables.

You have myClass object and pointer p (declared as MyClass *) created on stack. So. within myClass object the data members i also gets created on stack as part of myClass object. The pointer p is stored on stack, but you are not allocating for p, rather you are having p assigned to address of myClass object which is already stored on stack. So, no heap allocation here, at least from the program segment that you posted.

myClass is allocated on the stack. This program doesn't use heap exept maybe for allocating a stdout buffer behind the scenes. When myClass goes out of scope (e.g. when main returns or throws an exception), myClass is destructed and the stack is released, making p invalid.

In modern C++ it is considered safer to use smart pointers, such as the shared_ptr, instead of raw pointers.
To allocate myClass on the heap you could write:

#include <memory>

int main() {
  std::shared_ptr<MyClass> p (new MyClass (100));  // Two heap allocations: for reference counter and for MyClass.
  auto p2 = std::make_shared<MyClass> (101);  // One heap allocation: reference counter and MyClass stored together.
  return 0;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top