Beginner's Guide
    First Steps
    Input & Output
    Custom Types – Part 1
    Diagnostics
    Standard Library – Part 1
    Function Objects
    Standard Library – Part 2
    Code Organization
    Custom Types – Part 2
    Generic Programming
    Memory Management
    Software Design Basics

    Pointers Pointers Pointers

    Why do we need them? Why?

    • indirection without copying: referencing / keeping track of objects
    • want to change target objects at runtime ⇒ can't use references
    • access objects of dynamic storage duration i.e., objects whose lifetime is not tied to a variable / a scope (in later chapters)
    visualization of the indirection structure of some node based data structures

    Pointer to Object of Type T Pointer to T


    • essentially an (unsigned) integer variable storing a memory address
    • size: 64 bits on 64 bit platforms
    • many raw pointers can point to the same address / object
    • lifetimes of pointer and target object are independent

    Smart Pointers C++11


    We will learn how to use these smart pointers in later chapters.

    Operators &, * and -> Operators

    Address Operator & &

    Dereference Operator * *

    Member Access Operator -> ->

    Syntax Syntax

    * &
    Type Modifier

    Pointer Declaration

    Type* ptr = nullptr;

    Reference Declaration

    Type& ref = variable;
    Unary Operator

    Dereferencing

    value = *pointer;

    Taking Address

    pointer = &variable;
    Binary Operator

    Multiplication

    product = expr1 * expr2;

    Bitwise AND

    bitand = expr1 & expr2;
    int*  p1, p2;    // int*, int
    int  *p1, *p2;   // int*, int*
    int* p1 = …;
    int* p2 = …;

    Redirection

    int a = 0;
    int b = 0; 
    int* p = &a;
    *p = 2;
    p = &b;
    *p = 9;
    cout << a;  // 2
    cout << b;  // 9
    
    
          a: 0    b: 0
    p→a   a: 0    b: 0
    p→a   a: 2    b: 0
    p→b   a: 2    b: 0
    p→b   a: 2    b: 9
    
    

    nullptr nullptr C++11

    • special pointer value
    • is implicitly convertable to false
    • not necessarily represented by 0 in memory! (depends on platform)
    • set pointer to nullptr or valid address on initialization
    • check if not nullptr before dereferencing
    int* p = nullptr;   // init to nullptr
    if () {                             
      int i = 5;
      p = &i;  // assign valid address
      
      // check before dereferencing!
      if (p) *p = 7;  
      
      // set to nullptr, signalling 'not available'
      p = nullptr;   
    }                             
    // i's memory is freed, // any pointer to i would be invalidated!

    const and Pointers const

    1. read-only access to objects
    2. preventing pointer redirection

    Syntax

    pointer to type T pointed-to value modifiable pointer itself modifiable
    T *
    T const *
    T * const
    T const * const

    Examples

    int i = 5;
    int j = 8;
    int const* cp = &i;
    *cp = 8;   //  COMPILER ERROR: pointet-to value is const
    cp = &j;   //  OK
    int *const pc = &i;
    *pc = 8;   //  OK
    pc = &j;   //  COMPILER ERROR: pointer itself is const
    int const*const cpc = &i;
    *cpc = 8;  //  COMPILER ERROR: pointet-to value is const
    cpc = &j;  //  COMPILER ERROR: pointer itself is const

    Style: East const vs. const West An ongoing debate about style… Style

    one consistent rule:
    What's left of const is constant

    (still) more widespread, but less consistent

    int const c = ;
    int const& cr = ;
    int const* pc = ;
    int *const cp = ;
    int const*const cpc = ;
    const int c = 1;
    const int& cr = ;
    const int* pc = ;
    int *const cp = ;
    const int *const cpc = ;

    The this Pointer this

    • available inside member functions
    • this returns the address of an object itself
    • this-> can be used to access members
    • *this accesses the object itself
    class IntRange {
      int l_ = 0;
      int r_ = 0;
    public:
      explicit
      IntRange (int l, int r): l_{l}, r_{r} {
        if (l_ > r_) std::swap(l_, r_);
      }
      int left ()  const { return l_; }
      // can also use 'this' to access members:
      int right () const { return this->r_; }
      
      // returns reference to object itself
      IntRange& shift (int by) {
        l_ += by;
        r_ += by;
        return *this;
      }
      IntRange& widen (int by) {
        l_ -= by;
        r_ += by;
        return *this;
      }
    };
    
    IntRange r1 {1,3};
    r1.shift(1);
    r1.shift(2).widen(1);  // chaining possible!
    1 3
    2 4
    3 7

    Forward Declarations of Types Forward Declarations Declarations

    // forward declaration
    class Hub;
    class Device {
      Hub* hub_;
      
    };
    class Hub {
      std::vector<Device const*> devs_;
      
    };
    a number of hub and device objects pointing to each other

    In order to define a type, the memory sizes of all its members must be known.

    This in turn is only possible if the full definition of all members is known.

    However, all pointer types have the same size
    ⇒ we can just declare the existence of Hub, because Device only needs a pointer to it.

    Avoid Pointers If Possible Try to Avoid Pointers Avoid Pointers!

    • value stored in pointer can be any address
    • programmer has to make sure pointer target is valid / still exists
    int* p;  // p not initialized!
    *p = 7;  //  UNDEFINED BEHAVIOR
    p = nullptr;  
    *p = 7;  //  UNDEFINED BEHAVIOR access to nullptr
    {
      int x = 8;  
      p = &x;   
    }        // x's lifetime ends
    *p = 7;  //  UNDEFINED BEHAVIOR access to freed memory
    void swap_values (int* a, int* b) {
      int t = *a;
      *a = *b;
      *b = t;
    }
    int x = 3, y = 4;
    swap_values(&x, &y)        // OK
    swap_values(&x, 0);        //  UNDEFINED BEHAVIOR
    swap_values(&x, nullptr);  //  UNDEFINED BEHAVIOR
    *p = *p * *p + (2 * *p + 1);   // SO MANY STARS!