Week 4 Lecture Summary for CSC 309
These notes are not intended to be complete. They serve as an outline to the class and as a supplement to the text.
The Stack vs The Heap
- The stack is the name for an area of memory.
- Non-static local variables and function parameters live on the stack.
- Variables allocated on the stack don't persist. When a function returns the variables are tossed away.
- Storage requirements for stack variables are determined at compile-time.
- The heap, also called the free store is another name for an area of memory.
- Storage requirements for the heap are determined at run-time. We refer to this memory as dynamically allocated memory
- Objects allocated on the heap do not have names and are referred to by their address.
- Objects allocated on the heap persist until the program ends or until the programmer explicitly frees the memory.
- Memory allocated on the heap that is no longer accessible and not freed is called a memory leak
- To allocate memory on the heap we use the
new
operator. - To return the memory to the heap we use the
delete
operator.
Example
int *count = new int; *count = 7; delete count; double *score = new double; *score = 76.6; score = new double; // memory leak;
Dynamic Arrays
- Static arrays have some limitations.
- The length is fixed, cannot grow.
- The length needs to be known at compile time.
- The array only lives as long as the block in which it is defined.
- A dynamic array does not have these limitations.
- It's length can be determined at run time.
- Can continue to exist outside the block it was defined in.
Example of persistency problem
int* clone(int *scores, int size); void print(const int *scores, int size); int main(int argc, char *argv[]) { const int LEN = 9; int scores [LEN] = {1,2,3,4,5,6,7,8,9}; print(scores, LEN); int *copyScores = clone(scores, LEN); print(copyScores,LEN); system("PAUSE"); return EXIT_SUCCESS; } int* clone (int *scores, int size){ int copy[size]; for(int i = 0; i < size; i++) { *(copy + i) = *(scores + i); } return copy; } void print(const int *scores, int size){ const char space = ' '; for(int i = 0; i < size; ++i){ cout <<*(scores + i) << space; } cout << endl; return; }
Create and Destroy a Dynamic Array.
int *count = new int [20]; double *scores = new double [5]; delete [] count; delete [] scores;
Solution to our persistency problem
int* clone(int *scores, int size); void print(const int *scores, int size); int main(int argc, char *argv[]) { const int LEN = 9; int scores [LEN] = {1,2,3,4,5,6,7,8,9}; print(scores, LEN); int *copyScores = clone(scores, LEN); print(copyScores,LEN); delete [] copyScores; // FREE THE MEMORY system("PAUSE"); return EXIT_SUCCESS; } int* clone (int *scores, int size){ int *copy = new int [size]; // DYNAMIC ARRAY ALLOCATION for(int i = 0; i < size; i++) { *(copy + i) = *(scores + i); } return copy; } void print(const int *scores, int size){ const char space = ' '; for(int i = 0; i < size; ++i){ cout << *(scores + i) << space; } cout << endl; return; }
Classes and Objects
- So far we have been using the built in types or types that come with the standard library.
int double istream vector etc..
- Using the class (and struct) construct C++ allows us to define our own types.
- A class also creates a new scope.
- A class defines data (variable declarations) and operations or member functions.
- A class is a prototype for producing objects.
- An object is an instance of a class
- Object Oriented Programming is a style of programming that consists of interacting objects.
- We will build our types to obey the basic design principles.
- Encapsulation - Bundling together all the capabilities and responsibilities of an entity into a single object.
- Abstract Data Type - Exposes only the high level operations, the interface and hides the low level implementation details.
- Information Hiding is the hiding of design decisions that are most likely to change, thus protecting other parts of the program from a change if any changes were to occur.
The vector
class is an example of a well constructed type that is abstract and encapsulated.
We do not know how the vector is implemented. We do not have access to that implementation. As clients of the class
we typically do not care. We need to know what it can do and how to use it.
Defining a Class
Consider an ADT used to create Rectangle Objects
class Rectangle { // data declarations and operations go here };
- class - C++ keyword.
- { } indicate the scope of the class
- The semicolon ; after the closing brace is required
Defining an Object
You define an object of your new type similar in form to the built in types.
int count; string name; Rectangle room; Rectangle yards[100]; // creates 100 rectangle objects.
The class definition must come before the creation of any objects.
The Complete Rectangle ADT
class Rectangle { private: double length; double width; public: // Constructors Rectangle(){ length = 0.0; width = 0.0; } Rectangle(double l, double w) { length = l; width = w; } // Accessors double getLength(){ return length; } double getWidth(){ return width; } // Mutators or Transformers void setLength(double l){ length = l; } void setWidth(double w){ width = w; } // Other operations double computeArea(){ return length * width; } double computePerimeter(){ return 2 * length + 2 * width; } };
- The words private and public are C++ keywords. Referred to as visibility modifiers or access specifiers
- Private is used to hide class members from the outside world. These members are not-accessible outside the class.
- Public exposes members to the outside world, thus are accessible outside the class through any object of the class.
- To access members outside the class through an object we use the dot . operator. Also called the member access operator.
- Member functions with the same name as the class are called constructors.
- A constructor is executed automatically whenever we create an object of the class type.
- Constructors are used to give the object an intial state.
- Constructors have no return type specified.
- Class constructors can be overloaded.
int main(){ Rectangle room(45,20); double w = room.width; // error width is private w = room.getWidth(); // okay, returns 20 Rectangle square(5,5); cout << square.computeArea(); // prints 25; Rectangle yard; cout << yard.computePerimeter(); // prints 0 yard.setLength(3); yard.setWidth(10); cout << yard.computePerimeter(); // prints 26 computeArea(); // error, not a global function; system("pause"); return 0; }
Defining Class Methods
- The above Rectangle class defined its methods inside the class definition.
- A method defined inside the class definition is said to be inline.
- An inline method (or function) requests that the compiler place the method body wherever the method is invoked.
- The definition for a method may occur outside the class definition.
class Rectangle { private: double length; double width; public: // Constructors Rectangle(); Rectangle(double l, double w); // Accessors double getLength(); double getWidth(); // Mutators or Transformers void setLength(double l); void setWidth(double w); // Other operations double computeArea(); double computePerimeter(); }; Rectangle::Rectangle() { length = 0.0; width = 0.0; } Rectangle::Rectangle(double l, double w) { length = l; width = w; } double Rectangle::getLength(){ return length; } void Rectangle::setLength(double l){ length = l; } : :
- The class definition contains the function prototypes.
- Implementation uses the scope resolution operator :: to define the functions
- The return type is listed first.
An IntegerSet ADT
Design a class called IntegerSet that has an array of type boolean as an instance member. Each IntegerSet object will represent a set of integers in the range 0 - 100 (inclusive). Instead of storing the integers in an integer array, we will use an array of type boolean. An integer i is in the set if elements[i] is true, where elements is the name of your array.
If 50 is in the set then elements[50] would be true If 50 is not in the set then elements[50] would be false.
Before we design the IntegerSet class, it is helpful to look at some test code.
IntegerSet set; set.add(3); set.add(5); set.add(9): set.contains(5); // returns true set.toString(); // returns 3 5 9
A Complex Number Class (ADT)
Design a class that can be used to create Complex number objects. A complex number is a number of the form.
a + bi, where a,b are real numbers and i^2. = -1.a is called the real part and b the imaginary part.
Examples
2 + 3i 1.5 + -4.5i 3.0 + 0iComplex numbers can be added and subtracted.
(2 + 3i) + (5 + 6i) = 7 + 9i (3 + 5i) - (1 + 3i) = 2 + 2i
Also two complex numbers are equal if they have the same real and imaginary parts.
(2 + 3i) is equal to (2 + 3i)
Notice that 'i' is just a placeholder used to separate the real part (2) from the imaginary part(3).
Before we design the Complex number class, it is helpful to look at some test code.
Complex c1(2,3), c2(1.5, -4.5), c3(3.0), c4, c5; cout << c1.toString(); // prints 2 + 3i cout << c3.toString(); // prints 3 cout << c4.toString(); // prints 0 c5 = c1.plus(c2); cout << c5.toString(); // prints 3.5 + -1.5 c4.setReal(2).setImag(3); cout << c4.toString(); // prints 2 + 3i bool isEqual = c1.equals(c4); // returns true; isEqual = c1.equals(c2); // returns false;