Discussion 1
Discussion 1 delves into primitive types and gives a basic review of C++ from elementary programming classes. Moreover, it will help you get setup on the University of Michigan server, basic Linux commands, and compiling C++ code on CAEN.
Discussion 2
Discussion 2 talks about more basic C++ concepts such as if statements and conditional flow, as well as loops. It reiterates things like connecting to the remote CAEN server at Michigan.
Discussion 3
Discussion 3 dives into arrays and GDB. Unfortunately, the lecture recording, where memory diagrams of arrays were drawn on the board and walk through examples for GDB, could not be posted. Example, walk through code is posted below.
#include <iostream>
using namespace std;
// Global Constants
const int ROWSIZE = 3;
const int COLSIZE = 5;
// function definitions
void timesTwo(int size, int arr[]);
void printArray2D(int arr[][colsize], const int rows);
// main
int main(){
// declaring a 1d array
int arr1D[5] = {1,2,3,4,5};
// multiplying all the values by 2
timesTwo(SIZE, arr1D);
// initializing a 2d array size 3 by 5
int arr2D[3][5];
for(int i = 0; i < 3; ++i){
for(int j = 0; j < 5; ++j){
arr2D[i][j] = 5 * i + j;
}
}
// print 2d array out
printArray2d(arr2D);
return 0;
}
void timesTwo(int size, int arr[]){
for(int i = 1; i < size; ++i){
arr[i] *= 2;
}
}
void printArray2D(int arr[][]){
for(int r = 0; r < ROWSIZE; ++r){
for(int c = 0; c < COLSIZE; ++c){
cout << arr[r][c] << " ";
}
cout << endl;
}
}
Discussion 4
In Discussion 4 we delved into classes and walked through an example of classes. The starter code we wrote is below, and so is certain project related questions at the bottom of the .cpp file
#include <iostream> using namespace std; //Name: Faruk Kurtovic and Aneesh Rastogi //Unique Name: kurtovic@umich.edu aneeshr@umich.edu //project: Discussion4 example // this project attempts to create Object Oriented class // and building abstractions //Global Constants const int MIN_SEATS = 0; const int MAX_SEATS = 400; //biggest classroom - 400 seats (Stamps Auditorium) const int BUILDING_FLOORS = 3; const int ROOMS_PER_FLOOR = 10; /// /// class declarations at top of file before main() // ClassRoom object to represent a single classroom class ClassRoom{ private: bool built; int chairCount; bool inUse; public: // default constructor -- 'built' attribute is false // but when this is called, a ClassRoom object is created in C++ ClassRoom(); // non default constructor ClassRoom(int numberOfChairs, bool beingUsed); // copy ctor ClassRoom(ClassRoom & otherRoom); // dont implement -- but think about it's usefulness later // bool setChairAndInUse(int numChairs, bool beingUsed); // returns true if this ClassRoom is already 'built' bool isBuilt(); // gets the number of chairs int getChairCount(); // sets the amount of chairs in the room returns true if clipped bool setChairCount(int chairs); // increases the number of chairs by delta chairs returns true if clipped bool increaseChairs(int deltaChairs); // returns true if classroom is being used bool isBeingUsed(); // returns true if its possible to start a lecture - false if already has lecture bool startLecture(); // ends lecture if there is one - doesnt do anything if no lecture void endLecture(); }; //class declaration to represent entire building with floors and rooms class Building{ private: // 2d array of classRooms ClassRoom rooms[BUILDING_FLOORS][ROOMS_PER_FLOOR]; // number of rooms that exist int numBuiltRooms; // number of rooms that are used int numRoomsUsed; public: Building(); //a building starts with none of it's room as 'built' // ~Building(); // Building(Building& otherBuilding); only if there's time at end <- copy constructor // prints out numRoomsUsed and numBuiltRooms void printStats(); // Function returns the number of chairs within a certain room int getChairs(int floor, int roomNum); // Function sets the number of chairs within a room // Returns true if chairs need to be 'clipped' bool setChairs(int floor, int roomNum, int seats); // Functions fills chairs with bool increaseChairs(int floor, int roomNum, int deltaChairs); // creates classRoom initializes values within a class from values --> ClassRoom now 'built' bool createClassRoom(int floor, int roomNumber, int chairs, bool inUse); // create classRoom intializes a new classroom from an existing class --> ClassRoom now 'built' bool createClassRoom(int floor, int roomNumber, ClassRoom & newClass); // empty values within class Room, return false if the classRoom is not built bool clearClassRoom(int floor, int roomNumber); // occupyClassroom fills up the room in the building, returns false if the classRoom is not built bool occupyClassRoom(int floor, int roomNumber); // ClassRoom& getClassRoom(int floor, int roomNumber); DONT DO, BUT THINK ABOUT IT --> why is it useful? why is it bad? }; int main(){ Building building; building.printStats(); // fill building with empty rooms, with classes, of max chairs for(int i = 0; i < BUILDING_FLOORS; ++i){ for (int j = 0; j < ROOMS_PER_FLOOR; ++j) { building.createClassRoom(i, j, MAX_SEATS, true); } } building.printStats(); // check to see if we can add if(building.increaseChairs(0, 0, 1)){ cout << "Success had to clip to increase chairs" << endl; } else { cout << "failure" << endl; } cout << building.getChairs(0,0) << endl; building.setChairs(0,0,0); cout << building.getChairs(0,0) << endl; cout << "finishing from main function" << endl; return 0; } // default class constructor -- it's not built ClassRoom::ClassRoom(){ inUse = false; numChairs = 0; built = false; } // non default constructor ClassRoom::ClassRoom(int numberOfChairs, bool beingUsed){ inUse = beingUsed; numChairs = numberOfChairs; built = false; } // copy ctor ClassRoom::ClassRoom(ClassRoom & otherRoom){ inUse = otherRoom.inUse; numChairs = otherRoom.numChairs; built = otherRoom.built; } //returns if classroom is built bool ClassRoom::isBuilt(){ return built; } // returns number of chairs int ClassRoom::getChairCount(){ return numChairs; } // sets the number of chairs in a classroom // returns true if clipped bool ClassRoom::setChairCount(int chairs)//returns true if seatCount is lower/greater than min/max -"clipping" { // numChairs should be equal to chairs // but we have a limit on numChairs if (chairs <= MAX_SEATS && chairs >= MIN_SEATS){ numChairs = chairs; return false; } else if(chairs > MAX_SEATS){ chairs = MAX_SEATS; return true; } chairs = MIN_SEATS; return true; } // increase number of chairs by delta chairs // returns true if clipped bool ClassRoom::increaseChairs(int deltaChairs){ newNumChairs = numChairs + deltaChairs; return setChairCount(newNumChairs); } // returns true if classRoom is in use bool ClassRoom::isBeingUsed(){ } // returns if its possible to start a lecture bool ClassRoom::startLecture(){ } // ends lecture if running void ClassRoom::endLecture(){ } //class definition to represent entire building with floors and rooms Building::Building(){ //a building starts with none of it's room as 'built' numBuiltRooms = 0; numRoomsUsed = 0; } // prints out values void Building::printStats(){ cout << numBuiltRooms << endl; cout << numRoomsUsed << endl; } // Building(Building& otherBuilding); only if there's time at end int Building::getChairs(int floor, int roomNum){ return rooms[floor][roomNum].getChairs(); } // sets the chairs in a room // returns true if clipped bool Building::setChairs(int floor, int roomNum, int seats){ } // increases the chairs in a room by a certain amount // returns true if clipped bool Building::increaseChairs(int floor, int roomNum, int deltaChairs){ } // if we can make a a room, make a room // returns falls if not possible bool Building::createClassRoom(int floor, int roomNumber, int chairs, bool inUse){ } // returns true if we can createClassRooms bool Building::createClassRoom(int floor, int roomNumber, ClassRoom & newClass){ } // returns true if you can clearClass Room // checks to see if room is valid bool Building::clearClassRoom(int floor, int roomNumber){ } // retursn true if room is occupied // returns false otherwise bool Building::occupyClassRoom(int floor, int roomNumber){ } // ClassRoom& getClassRoom(int floor, int roomNumber); DONT DO, BUT THINK ABOUT IT --> why is it useful? why is it bad? //Project stuff: /* - in the project, we have an object (class) RowColClass which stores a row and col used to index into an image (2D array) --> functions that need to access a pixel have a 'RowColClass' parameter passed in to know which pixel to use/update in the 2D array - bool ColorImageClass::setColorAtLocation(RowColumnClass &inRowCol,ColorClass &inColor) "the inRowCol parameter is passed by reference, but we dont need to change it--> why is it passed by reference?" --> "efficiency" --> probably should be const RowColumnClass &inRowCol (BUT DO NOT CHANGE IT IN THE PROJCET) */
Discussion 5
This discussion delves into .h and .cpp files and how constructors and destructors run. Moreover, it talks about getters and setters in classes, as well as header gaurds, and strings in C++ and C. Sample Code is posted below to follow along:
////////////////////////////////////////////////////// ////////////////// FIRSTGRADER.H ///////////////////// ////////////////////////////////////////////////////// // #include <iostream> #ifndef FIRST_GRADER_H #define FIRST_GRADER_H class first_grader{ private: int age; public: //set private attribute 'age' void setAge(const int ageIn); //get age function -- doesnt modify data -> const function int getAge() const ; //default ctor -- necessary since at least one non-default ctor exists first_grader(); //non-default ctor first_grader(int ageIn); //copy ctor first_grader(const first_grader &other); //destructor -- no necessary, but useful for examples ~first_grader(); }; #endif ////////////////////////////////////////////////////// ///////////////// FIRSTGRADER.CPP //////////////////// ////////////////////////////////////////////////////// #include "first_grader.h" #include <iostream> using namespace std; // #include <iostream> first_grader::first_grader() { cout << "default ctor" << endl; } first_grader::first_grader(const int ageIn) { cout << "nondefault ctor" << endl; age = ageIn; } first_grader::first_grader(const first_grader &other) { cout << "copy ctor" << endl; //below is valid // age = otherFirstGrader.getAge(); age = other.age; } void first_grader::setAge(const int ageIn) { cout << "set age" << endl; age = ageIn; } int first_grader::getAge() const { cout << "get age" << endl; return age; } first_grader::~first_grader() { cout << "~ destructor ... age = " << age << endl; //nothing -- becomes more useful with dynamic memory }
Discussion 6
In Discussion 6, I we talked about dynamic memory and how it differs from variables declared in the scope of a function. Moreover, we talked about using dynamic memory, pointers, and dynamic arrays.
Discussion 7
In Discussion 7 we covered basic Object Oriented Programming Design as well as we whiteboard coded how to solve two basic problems. Moreover, we talked about the use of dynamic arrays. Unfortunately, sample code cannot be posted.
Discussion 8
In Discussion 8, we talked about Linked Lists and Node classes. Moreover, we delved into some basic functions like insert and delete. A sample singly linked list starter code is implemented below in 4 different files:
//////////////// //// Node.h //// //////////////// #ifndef _NODE_H_ #define _NODE_H_ using namespace std; class Node{ // private: public: char value; Node* next; // public: // // Non-default Constructor // Node(char val); // // gets the next pointer // Node* getNext(); // // sets the next pointer // void setNext(Node* in); // // gets the value of node // char getVal(); // // sets the value of the node // void setVal(char in); }; #endif //////////////// /// Node.cpp /// //////////////// #include "Node.h" using namespace std; // Node::Node(char in){ // } // Node* Node::getNext(){ // } // char Node::getVal(){ // } // void Node::setVal(char in){ // } // void Node::setNext(Node* in){ // } //////////////// //// List.h //// //////////////// #ifndef _LIST_H_ #define _LIST_H_ #include "Node.h" using namespace std; class List{ private: Node* head; Node* tail; public: List(); // List(char val); //add node with val to end void insert(char val); void operator +=(char val); void changeVal(int index, char val); //print content of each node // ( value ) --> ( value ) --> ... void printList(); void erase(int index); ~List(); //advanced practice: do at home // void insert(int index, char val); }; #endif //////////////// /// List.cpp /// //////////////// #include <iostream> using namespace std; #include "List.h" //create empty list List::List(){ } //create list with one node containing val // List::List(char val){ // } //create node at end of LL, init to val void List::insert(char val){ } //create node at end of LL, init to val void List::operator +=(char val){ } //chand the val inside node at place 'index' void List::changeVal(int index, char val){ } //print the list void List::printList(){ } //erase the node at the 'index' spot void List::erase(int index){ } //free all the memory associated with this list //clear up everythign on the heap List::~List(){ }
Discussion 9
Quiz given on stacks and queues in discussion: Assume a correctly implemented Stack, Queue, LinkedList Stack has the functionality: .push(int val) // pushes value into the stack .pop() // removes from top of stack .empty() // returns true if stack is empty .top() //returns top of stack Queue has the functionality: .push(int val) // pushes value into the queue .pop() // removes from front of queue .empty() // returns true if the queue is empty .front() // returns front of queue LinkedList has the functionality: += operator() // adds to the *FRONT* of the list .insert(int val, int index) // adds a value at an index .erase(int index) // removes a value at an index .getHead() // returns value at head .getTail() // returns value at tail int main () { Stack A; for ( int i = 4 ; i > 0 ; i--) { A.push(i); } while ( A.empty() == false ) { cout<<A.top()<<endl; A.pop(); } Queue B; for ( int i = 4 ; i > 0 ; i-- ) { B.push(i); } while ( B.empty() == false ) { cout<< B.top()<<endl; B.pop(); } LinkedList C; for ( int i = 4; i > 0 ; i-- ) { C += i; //NOTE: post fix operator } cout<<"Ends of C:"<<C.getHead()<<", "<<C.getTail()<<endl; C.insert(6, 0); cout<<"Ends of C"<<C.getHead()<<" "<<C.getTail()<<endl; C.insert(9, 4); cout<<"Ends of C"<< C.getHead()<<" "<<C.getTail()<<endl; while ( C.empty() == false ) { cout<<C.getHead()<<endl; C.erase(0); } return 0; }
In Discussion 9, we covered stacks, queues and the big three. Also, there was quiz given in class. Below is sample code for a Linked List:
///////////////// /////List.h////// ///////////////// #ifndef _LIST_H_ #define _LIST_H_ // Implemenatation of a Singly Linked-List using namespace std; class Node { //SHOULD BE IN SEPERATE FILE -- VIOLATION OF DESIGN // done for simplicity public: char value; //make private with get() and set() Node* next; //make private with get() and set() }; class List{ private: Node * firstNodePointer; //"head" Node * lastNodePointer; //"tail" int length; public: List(); // add 'val' to end of list (THE FOLLOWING TWO ARE EQUIVALENT) void append(const char val); void operator +=(const char val); //change the ith value in the list : (0-based) bool changeVal(const int index, const char newval); //print content of entire list... each node (THE FOLLOWING TWO ARE EQUIVALENT) void printList(); // ( value ) --> ( value ) --> ... // ostream& operator<<(ostream & outputstream); // ( value ) --> ( value ) --> ... bool erase(int index); //remove ith node from link list ~List(); //"clean up" : destroy dynamic memory }; #endif ///////////////// ////List.cpp///// ///////////////// #include <iostream> using namespace std; #include "List.h" // at any point, are list will look something like this: // O ---> O ---> O ---> O ---> (NOTHING ... address 0) // where each O represents a node with a value, and "--->" represents that the next // pointer of each node points to the "next" node in the chain (using next pointer) List::List() { firstNodePointer = 0; //set head & tail node * to point to address 0 lastNodePointer = 0; length = 0; //list starts empty, length = 0 } // add 'val' to end of list (THE FOLLOWING TWO ARE EQUIVALENT) void List::append(const char val) { //create a new Node, dynamically Node * newNode = new Node; newNode -> value = val; //initialize newNode -> next = 0; // newNode added to end of list, points to nothing if (length == 0) { //list is empty: length == 0, tail = 0, head = 0 firstNodePointer = newNode; lastNodePointer = newNode; length = 1; } else { // length > 0: head != 0 & tail != 0 .... there is at least one Node in the list lastNodePointer -> next = newNode; lastNodePointer = newNode; ++length; } //Question: does "newNode" go out of scope now? // why is this good/bad? } //THIS CODE IS DUPLICATED FROM "append(val)" --> in practice, make private method, call from both void List::operator +=(const char val){ //create a new Node, dynamically //IDEA: put next three lines in constructor for Node Node * newNode = new Node; newNode -> value = val; //initialize newNode -> next = 0; // newNode added to end of list, points to nothing if (length == 0) { //list is empty: length == 0, tail = 0, head = 0 firstNodePointer = newNode; lastNodePointer = newNode; length = 1; } else { // length > 0: head != 0 & tail != 0 .... there is at least one Node in the list lastNodePointer -> next = newNode; lastNodePointer = newNode; ++length; } //Question: does "newNode" go out of scope now? // why is this good/bad? } //change the ith value in the list : (0-based) //returns true if value successfully changed to 'val' bool List::changeVal(const int index, const char newval){ // if ( index >= length ) { //index beyond length of list // return false; // } int idx = 0; bool changed = false; Node * iteratePointer = firstNodePointer; while ( idx < index && iteratePointer != 0 ) { iteratePointer = iteratePointer -> next; //move on to "next" node ++idx; //keep track of how many nodes visited } if ( idx == index && iteratePointer != 0 ) { iteratePointer -> value = newval; //update value changed = true; } return changed; } //print content of entire list... each node (THE FOLLOWING TWO ARE EQUIVALENT) void List::printList(){ // ( value ) --> ( value ) ---> ... Node * nPointer = firstNodePointer; if ( length == 0 ) { cout << " < empty list > " << endl; return; } while (nPointer != 0 ){ cout << " ( " << nPointer -> value << " ) --->"; nPointer = nPointer -> next; } cout << " 0 address" << endl; } // return true if succesfully delete, false otherwise bool List::erase(int index) { // remove ith node from link list // approach: 1) find the node right before (predecessor to) the node we want to delete // 2) make a tempPointer to the thing in front // 3) make predecessor -> next = the "-> next " of what we want to delete // 4) delete tempPointer // // EDGE CASE: delete first node : index == 0 if ( index >= length ) { // if "index" greater than index of last node return false; } if ( index == 0 ) { //deleting first thing in list Node * toDelete = firstNodePointer; if ( length == 1 ) { // there was only one node in the list --> now it's empty lastNodePointer = 0; } else { //do nothing } firstNodePointer = firstNodePointer -> next; delete toDelete; toDelete = 0; --length; //update number of nodes //CHECK YOUR UNDERSTANDING : Why do we need "toDelete" ? // Why not just delete firstNodePointer ? } else { //it's within the list somewhere Node * predecessor = firstNodePointer; //start at beginning int idx = 0; while ( idx < index - 1 ) { predecessor = predecessor -> next; ++idx; } Node * toDelete = predecessor -> next; if ( index == length - 1 ) { //if removing last node -- requires that length is always correct ! //if ( predecessor -> next == lastNodePointer ) { //equivalent lastNodePointer = predecessor; //new tail is thing before old tail predecessor -> next = 0; //CHECK YOUR UNDERSTANDING: // What if we didn't check this ? What would happen ? What invariant is broken ? } else { predecessor -> next = toDelete -> next; toDelete -> next = 0; } delete toDelete; toDelete = 0; --length; } return true; } List::~List(){ //"clean up" : destroy dynamic memory Node * iter = firstNodePointer; while ( iter != 0 ) { Node * temp = iter; iter = iter -> next; temp -> next = 0; delete temp; temp = 0; --length; } firstNodePointer = lastNodePointer = 0; }
Discussion 10
In Discussion 10, we delved into binary search trees and templating.
Discussion 11
In Discussion 11, we talk about the standard templated library and different data structures available. We also delve into functors used for custom sorting. More resources can be found @ cppreference