A newer version of the Gradio SDK is available:
5.23.1
C++\
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 limited 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 scope
!---
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
Native type just like an int or long
Hold the location of another variable or object in memory
Useful in avoiding expensive copies of large objects
Facilitate shared memory
- Example: One object "owns" the memory associated with some data, and allows others 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
!---
Pointer Syntax (continued)
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 (Stroustrup), 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!
!---
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 (Stroustrup), so it must reference an 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 myshape, 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 allocations 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 overloaded 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".