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".