for Robot Artificial Inteligence

8. pointer, stack and heap, memory leaks, dangling pointers

|

Using pointers for classes

  • Pointers can point to objects of custom classes:
std :: vector <int> vector_int ;
std :: vector <int>* vec_ptr = & vector_int ;
MyClass obj;
MyClass* obj_ptr = &obj;
  • Call object functions from pointer with ->
    MyClass obj;
    obj.MyFunc();
    MyClass* obj_ptr = &obj;
    obj_ptr ->MyFunc ();
    
  • obj->Func() ↔ (* obj).Func()

Pointers are polymorphic

  • Pointers are just like references, but have additional useful properties:
    • Can be reassigned
    • Can point to ‘‘nothing’’ (nullptr)
    • Can be stored in a vector or an array
  • Use pointers for polymorphism
Derived derived;
Base* ptr = &derived;
  • Example: for implementing strategy store a pointer to the strategy interface and initialize it with nullptr and check if it is set before calling its methods
#include <iostream>
#include <vector>
using std::cout;
struct AbstractShape {
  virtual void Print () const = 0;
};
struct square : public AbstractShape{
  void Print () const override {cout << "Square";}
};
struct triangle : public AbstractShape{
  void Print () const override {cout << "triangle";}
};
int main () {
  std::vector<AbstractShape * > shapes;
  Square square;
  Triangle triangle;
  shapes. push_back (& square);
  shapes. push_back (& triangle);
  for (const auto* shape : shapes) { shape ->Print (); }
  return 0;
}

this pointer

  • Every object of a class or a struct holds a pointer to itself
  • This pointer is called this
  • Allows the objects to:
    • Return a reference to themselves: return * this;
    • Create copies of themselves within a function
    • Explicitly show that a member belongs to the current object: this->x();

Using const with pointers

  • Pointers can point to a const variable:
    // Cannot change value , can reassign pointer.
    const MyType* const_var_ptr = &var;
    const_var_ptr = & var_other ;
    
  • Pointers can be const:
    // Cannot reassign ponter , can change value.
    MyType* const var_const_ptr = &var;
    var_const_ptr ->a = 10;
    
  • Pointers can do both at the same time:
    // Cannot change in any way, read -only
    const MyType* const const_var_const_ptr = &var;
    
  • Read from right to left to see which const refers to what

Memory management structures

  • Working memory is divided into two parts:

Stack memory

  • Static memory
  • Available for short term storage (scope)
  • Small / limited (8 MB Linux typisch)
  • Memory allocation is fast
  • LIFO (Last in First out) structure
  • Items added to top of the stack with push
  • Items removed from the top with pop

  • result: 42 4952101

  • only pointing first array in here until it give a certain array index to pointer.

Heap Memory

  • Dynamic memory
  • Available for long time (program runtime)
  • Raw modifications possible with new and delete (usually encapsulated within a class)
  • Allocation is slower than stack allocations

Operators new and new[]

  • User controls memory allocation (unsafe)
  • Use new to allocate data:
    // pointer variable stored on stack
    int* int_ptr = nullptr;
    // 'new' returns a pointer to memory in heap
    int_ptr = new int;
    // also works for arrays
    float* float_ptr = nullptr;
    // 'new' returns a pointer to an array on heap
    float_ptr = new float[number ];
    
  • new returns an address of the variable on the heap
  • Prefer using smart pointers!
  • Memory is not freed automatically!
  • User must remember to free the memory
  • Use delete or delete[] to free memory: ```c++ int* int_ptr = nullptr; int_ptr = new int; // delete frees memory to which the pointer points delete int_ptr;

// also works for arrays float* float_ptr = nullptr; float_ptr = new float[number ]; // make sure to use ‘delete[]’ for arrays delete[] float_ptr ;

- Prefer using smart pointers!

### Example: heap memory
```c++
#include <iostream>
using std::cout; using std::endl;
int main () {
  int size = 2; int* ptr = nullptr;
  {
    ptr = new int[size ];
    ptr [0] = 42; ptr [1] = 13;
  }
  // End of scope does not free heap memory!
  // Correct access , variables still in memory.
  for (int i = 0; i < size; ++i) {
    cout << ptr[i] << endl;
  }
  delete[] ptr; // Free memory.
  for (int i = 0; i < size; ++i) {
    // Accessing freed memory. UNDEFINED!
    cout << ptr[i] << endl;
  }
  return 0;
}

Memory Leak

  • Can happen when working with Heap memory if we are not careful
  • Memory leak: memory allocated on Heap access to which has been lost

#include <iostream>
using std::cout; using std::endl;
int main () {
  double * ptr_1 = NULL;
  double * ptr_2 = NULL;
  int size = 10;
  // Allocate memory for two arrays on the heap
  ptr_1 = new double[size];
  ptr_2 = new double[size];
  cout << "1: " << ptr_1 << " 2: " << ptr_2 << endl;
  ptr_2 = ptr_1;
  // ptr_2 overwritten , no chance to access the memory.
  cout << "1: " << ptr_1 << " 2: " << ptr_2 << endl;
  delete[] ptr_1;
  delete[] ptr_2;
  return 0;
}

  • The memory under address 0x10a3070 is never freed
  • Instead we try to free memory under 0x10a3010 twice
  • Freeing memory twice is an error

Memory leak example

#include <iostream>
#include <cmath>
#include <algorithm>
using std::cout; using std::endl;
int main() {
  double * data = nullptr;
  size_t size = pow (1024 , 3) / 8; // Produce 1GB
  for (int i = 0; i < 5; ++i) {
    // Allocate memory for the data.
    data = new double[size ];
    std :: fill(data , data + size , 1.23);
    // Do some important work with the data here
    cout << "Iteration: " << i << " done!" << endl;
  }
  // This will only free the last allocation!
  delete[] data;
int unused; std :: cin >> unused; // Wait for user.
return 0;
}
  • result
    1 Iteration : 0 done!
    2 Iteration : 1 done!
    3 Iteration : 2 done!
    4 Iteration : 3 done!
    5 terminate called after throwing an instance of 'std ::
    bad_alloc '
    6 what (): std :: bad_alloc
    
  • If we run out of memory an std::bad_alloc error is thrown
  • Be careful running this example, everything might become slow

Dangling pointer(Memory Leak 반대개념)

int* ptr_1 = some_heap_address ;
int* ptr_2 = some_heap_address ;
delete ptr_1;
ptr_1 = nullptr;
// Cannot use ptr_2 anymore! Behavior undefined!
  • Dangling Pointer: pointer to a freed memory
  • Think of it as the opposite of a memory leak
  • Dereferencing a dangling pointer causes undefined behavior

    Dangling pointer example

    #include <iostream>
    using std::cout; using std::endl;
    int main () {
    int size = 5;
    int* ptr_1 = new int[size ];
    int* ptr_2 = ptr_1; // Point to same data!
    ptr_1 [0] = 100; // Set some data.
    cout << "1: " << ptr_1 << " 2: " << ptr_2 << endl;
    cout << "ptr_2[0]: " << ptr_2 [0] << endl;
    delete[] ptr_1; // Free memory.
    ptr_1 = nullptr;
    cout << "1: " << ptr_1 << " 2: " << ptr_2 << endl;
    // Data under ptr_2 does not exist anymore!
    cout << "ptr_2[0]: " << ptr_2 [0] << endl;
    return 0;
    }
    

    Even worse when used in functions

    #include <stdio.h>
    // data processing
    int* GenerateData(int size);
    void UseDataForGood (const int* const data , int size);
    void UseDataForBad (const int* const data , int size);
    int main () {
    int size = 10;
    int* data = GenerateData (size);
    UseDataForGood (data , size);
    UseDataForBad (data , size);
    // Is data pointer valid here? Should we free it?
    // Should we use 'delete[]' or 'delete '?
    delete[] data; // ?????????????
    return 0;
    }
    

Memory leak or dangling pointer

void UseDataForGood (const int* const data , int size) {
  // Process data , do not free. Leave it to caller.
}
void UseDataForBad (const int* const data , int size) {
  delete[] data; // Free memory!
  data = nullptr; // Another problem - this does nothing!
}
  • Memory leak if nobody has freed the memory
  • Dangling Pointer if somebody has freed the memory in a function

RAII

  • Resource Allocation Is Initialization.
  • New object → allocate memory
  • Remove object → free memory
  • Objects own their data!
    struct SomeOtherClass{};
    class MyClass {
    public:
    MyClass () { data_ = new SomeOtherClass ; }
    ~MyClass () {
      delete data_;
      data_ = nullptr;
    }
    private:
    SomeOtherClass * data_;
    };
    int main()
    {
    MyClass a; //copy constructure
    MyClass b(a); // copy object
    return 0;
    }
    
  • result
    • samd data allocated
      1 *** Error in `raii_example ':
      2 double free or corruption : 0 x0000000000877c20 ***
      

Shallow vs deep copy

  • Shallow copy: just copy pointers, not data
  • Deep copy: copy data, create new pointers
  • Default copy constructor and assignment operator implement shallow copying
  • RAII + shallow copy → dangling pointer
  • RAII + Rule of All Or Nothing → correct
  • Use smart pointers instead!

Reference

https://www.ipb.uni-bonn.de/teaching/modern-cpp/

Cpp Core Guidelines: https://github.com/isocpp/CppCoreGuidelines

Git guide: http://rogerdudler.github.io/git-guide/

C++ Tutorial: http://www.cplusplus.com/doc/tutorial/

Book: Code Complete 2 by Steve McConnell

Modern CMake Tutorial https://www.youtube.com/watch?v=eC9-iRN2b04

Compiler Explorer: https://godbolt.org/ Gdbgui: https://www.gdbgui.com/ CMake website: https://cmake.org/ Gdbgui tutorial: https://www.youtube.com/watch?v=em842geJhfk

Fluent C++: structs vs classes: https://google.github.io/styleguide/cppguide.html#Structs_vs._Classes

Comments