Scope, Memory and Overloading

Scope

  • A scope is the extent of the program where a variable can be seen and used.

    • Local variables have scope from the point of declaration to the end of the enclosing block {}

    • Global variables are not enclosed within any scope and are available within the entire file

  • Variables have a lifetime

  • When a variable goes out of scope, its destructor is called

  • Dynamically-allocated (via new) memory is not automatically freed at the end of the scope

"Named" Scopes

  • class scope

    
    class MyObject
    {
    public:
      void myMethod();
    };
    

  • namespace scope

    
    namespace MyNamespace
    {
      float a;
      void myMethod();
    }
    

Scope Resolution Operator

  • "double colon" :: is used to refer to members inside of a named scope

    
    // definition of the "myMethod" function of "MyObject"
    void MyObject::myMethod()
    {
      std::cout << "Hello, World!\n";
    }
    MyNamespace::a = 2.718;
    MyNamespace::myMethod();
    

  • Namespaces permit data organization, but do not have all the features needed for full encapsulation Assignment (Prequel to Pointers and Refs)

  • Recall that assignment in C++ uses the "single equals" operator:

    
    a = b; // Assignment
    

  • Assignments are one of the most common operations in programming

  • Two operands are required

    • An assignable location on the left hand side (memory location)

    • An expression on the right hand side

Pointers

  • Pointers are a native type just like an int or long

  • Pointers hold the location of another variable or object in memory

  • Pointers are useful in avoiding expensive copies of large objects

    • Ex : Functions are passed pointers to large objects, rather than the objects themselves

  • Pointers also facilitate shared memory

    • Ex : one object "owns" the memory associated with some data, and allows other objects access through a pointer

Pointer Syntax

  • Declare a pointer

    
    int *p;
    

  • Use the "address-of" operator to initialize a pointer

    
    int a;
    p = &a;
    

  • Use the "dereference" operator to get or set values pointed-to by the pointer

    
    *p = 5;                  // set value of "a" through "p"
    std::cout << *p << "\n"; // prints 5
    std::cout <<  a << "\n"; // prints 5
    
    
    int a = 5;
    int *p;       // declare a pointer
    p = &a;       // set 'p' equal to address of 'a'
    *p = *p + 2;  // get value pointed to by 'p', add 2,
                  // store result in same location
    std::cout <<  a << "\n";  // prints 7
    std::cout << *p << "\n";  // prints 7
    std::cout <<  p << "\n";  // prints an address (0x7fff5fbfe95c)
    

Pointers are Powerful but Unsafe

  • On the previous slide we had this:

    
    p = &a;
    
  • But we can do almost anything we want with p!

    
    p = p + 1000;
    

  • Now what happens when we do this?

    
    *p;    // Access memory at &a + 1000
    

References to the Rescue

  • A reference is an alternative name for an object (Think of it as an alias for the original variable)


int a = 5;
int &r = a;  // define and initialize a ref
r = r + 2;
std::cout <<  a << "\n";  // prints 7
std::cout <<  r << "\n";  // prints 7
std::cout << &r << "\n";  // prints address of a

References are Safe

  • References cannot be modified

    
    &r = &r + 1;   // won't compile
    

  • References never start out un-initialized

    
    int &r;     // won't compile
    

  • Note that class declarations may contain references

  • If so, initialization must occur in the constructor!

  • We will see an example later on...

Summary : Pointers and References

  • A pointer is a variable that holds a memory address to another variable

    
    int *iPtr;  // Declaration
    iPtr = &c;
    int a = b + *iPtr;
    

  • A reference is an alternative name for an object

    • Cannot create a reference without existing object

    
    int &iRef = c;    // Must initialize
    int a = b + iRef;
    

Calling Conventions

  • What happens when you make a function call


result = someFunction(a, b,my_shape);
  • If the function changes the values inside of a, b or my_shape, are those changes reflected in my code ?

  • Is this call expensive ? (Are arguments copied around ?)

  • C++ by default is "Pass by Value" (copy) but you can pass arguments by reference (alias) with additional syntax

Swap Example (Pass by Value)


void swap(int a, int b)
{
  int temp = a;
  a = b;
  b = temp;
}
int i = 1;
int j = 2;
swap (i, j);                  // i and j are arguments
std::cout << i << " " << j;   // prints 1 2
                              // i and j are not swapped

Swap Example (Pass by Reference)


void swap(int &a, int &b)
{
  int temp = a;
  a = b;
  b = temp;
}
int i = 1;
int j = 2;
swap (i, j);                  // i and j are arguments
std::cout << i << " " << j;   // prints 2 1
                              // i and j are properly swapped

Dynamic Memory Allocation

Why do we need dynamic memory allocation?

  • Data size specified at run time (rather than compile time)

  • Persistence without global variables (scopes)

  • Efficient use of space

  • Flexibility

Dynamic Memory in C++

  • "new" allocates memory

  • "delete" frees memory

  • Recall that variables typically have limited lifetimes (within the nearest enclosing scope)

  • Dynamic memory allocation do not have limited lifetimes

    • No automatic memory cleanup!

    • Watch out for memory leaks

    • Should have a "delete" for every "new"

  • During normal usage, dynamic memory allocation is unnecessary

Example: Dynamic Memory


int a;
int *b;
b = new int; // dynamic allocation, what is b's value?
a = 4;
*b = 5;
int c = a + *b;
std::cout << c;  // prints 9
delete b;

Example: Dynamic Memory Using References


int a;
int *b = new int;    // dynamic allocation
int &r = *b;         // creating a reference to newly created variable
a = 4;
r = 5;
int c = a + r;
std::cout << c;  // prints 9
delete b;

Const

  • The const keyword is used to mark a variable, parameter, method or other argument as constant.

  • Typically used with references and pointers to share objects but guarantee that they will not be modified


{
  std::string name("myObject");
  print(name);
  ...
}
void print(const std::string & name)
{
  // Attempting to modify name here will
  // cause a compile time error
  ...
}

Function Overloading

In C++ you may reuse function names as long as they have different parameter lists or types. A difference only in the return type is not enough to differentiate overload signatures.


int foo(int value);
int foo(float value);
int foo(float value, bool is_initialized);
...

This is very useful when we get to object "constructors".