Introduction to Object Oriented Programming

1. Youtube videos

These notes work best together with 5 youtube videos that I created about object oriented programming. The videos are similar to these notes, but have more details. This page contains the code that is developed in the videos. Here is the link to the 5 videos about object oriented programming.

2. Class Credit Card Account

The class CCAccount will have attributes that correspond to the name of the customer, the balance on the account, and the list of all transactions on the account. The attribute cName is of type std::string and contains the name of the customer. The attribute balance is of type double and contains the balance on the account. We will use the convention that a positive balance means that the customer owes money. The list of all transactions will be kept on stack. Since we are building a simplified example, we will not maintain the full transaction records such as date, time, and merchant. We will only keep track of the amount of the transaction.

We will keep all of these attributes private. The keyword private prevents the access to the attributes. Only the creator of the class can change the name of the customer and update transactions. These changes must be made within the methods of the class.

The users of the class are other programmers in the bank: think of front-end developers and programmers who are making online banking websites and apps. These programmers would need to read the names of customers, read and display the balances, but should be prevented from altering the names. They would be allowed to change the balance only by using specific methods in which they will supply the amount that is charged to the account and the class itself will properly update the stack and the balance.

The first version of the code is displayed below.

// ccexamples00.cpp
 // compile with
 // c++ ccexamples00.cpp -o cc00 -std=c++11
 // execute with
 // ./cc00

 #include<iostream>
 class SN{// Stack Node
 public:
  double content;
  SN* aNext;
 };
 SN* push(SN* oH, double x){
  SN* nN;
  nN=new SN;
  nN->content=x;
  nN->aNext=oH;
  return nN;
 }
 SN* pop(SN* oH){
  if(oH==nullptr){
    return nullptr;
  }
  SN* nextNode;
  nextNode=oH->aNext;
  delete oH;
  return nextNode;
 }
 double readTop(SN* aH){
  if(aH==nullptr){
    return 0.0;
  }
  return aH->content;
 }
 int isEmpty(SN* aH){
  if(aH==nullptr){
    return 1;
  }
  return 0;
 }
 void deleteStack(SN* aH){
  while(aH!=nullptr){
    aH=pop(aH);
  }
 }

 class CCAccount{ // Credit Card Account
 private:
  std::string cName;// Name of the customer
  double balance;
  SN* aHT;// address of the Head of the stack of Transactions
 public:
  CCAccount(std::string );
  std::string getName() ;
  double getBalance() ;
  void changeName(std::string, int);
  // The first argument is new name;
  // The second argument is a password -
  // the programmers without the correct password
  // cannot change the name
  void addCharge(double);
  void printStatement() ;// prints all transactions
  ~CCAccount();
};
 CCAccount::CCAccount(std::string  s){
  cName=s;
  balance=0.0;
  aHT=nullptr;
 }
 std::string CCAccount::getName(){
  return cName;
 }
 double CCAccount::getBalance(){
  return balance;
 }
 void CCAccount::changeName(std::string newName, int p){
  if(p==123){
    cName=newName;
  }
}
 void CCAccount::addCharge(double x){
  balance=balance+x;
  aHT=push(aHT,x);
 }
 void CCAccount::printStatement(){
  std::cout<< "The account holder "<< cName<< " has balance ";
  std::cout<< balance<< ".\n";
  std::cout<< "The transactions are: ";
  SN* runner;
  runner=aHT;
  while(runner!=nullptr){
    std::cout<< runner->content <<"\t";
    runner=runner->aNext;
  }
  std::cout << "\n";
 }
 CCAccount::~CCAccount(){
  std::cout<< "Deleting the account for "<< cName << ".\n";
  deleteStack(aHT);
 }
 int main(){
  CCAccount customerA("BSimpson");
  customerA.addCharge(18.99);customerA.addCharge(5.23);
  customerA.addCharge(18.29);
  customerA.printStatement();
  int uI;
  std::cout<< "Do you want to add another user? [1=yes, 0=no] ";
  std::cin>>uI;
  if(uI==1){
    CCAccount customerB("LSimpson");
    customerB.addCharge(20.01);customerB.addCharge(19.09);
    customerB.addCharge(79.95);
    customerB.printStatement();customerA.printStatement();
  }
  customerA.printStatement();
  return 0;
 }

3. Keyword const

To improve the communication between programmers, the language allows us to use the keyword const in front of variables. When const is placed in front of an argument of a function, then it is a promise that the function will not change the argument. If it does, the compiler will report an error and refuse to make the executable binary.

At first glance the keyword const offers no benefits to the programmers, just the trouble: it can cause the compilation to be terminated and it does not introduce the new features to the final code. The benefit is readability of the code. When someone else reads the code by seeing the word const in front of the variable that is passed by reference, then this programmer is sure that the function will not change the variable.

When the keyword const is used after the declaration of the method, then the creator of the method is making a promise that the method will not change any of the attributes. In other words, the method is "accessor" and not a "mutator". In fact a little stronger promise is made: the method will not only leave the attributes unchanged, it will also not call any other methods unless they are const. When making complex projects that involve multiple teams of programmers, individual contributors are often required to use the keyword const whenever possible.

In our next version of the code we will add this keyword as necessary.

4. Copy constructor and copy assignment

We will also create the copy constructor and copy assignment.

// ccexamples01.cpp
 // compile with
 // c++ ccexamples01.cpp -o cc01 -std=c++11
 // execute with
 // ./cc01

 #include<iostream>
 class SN{// Stack Node
 public:
  double content;
  SN* aNext;
 };
 SN* push(SN* oH, double x){
  SN* nN;
  nN=new SN;
  nN->content=x;
  nN->aNext=oH;
  return nN;
 }
 SN* pop(SN* oH){
  if(oH==nullptr){
    return nullptr;
  }
  SN* nextNode;
  nextNode=oH->aNext;
  delete oH;
  return nextNode;
 }
 double readTop(SN* aH){
  if(aH==nullptr){
    return 0.0;
  }
  return aH->content;
 }
 int isEmpty(SN* aH){
  if(aH==nullptr){
    return 1;
  }
  return 0;
 }
 void deleteStack(SN* aH){
  while(aH!=nullptr){
    aH=pop(aH);
  }
 }

 SN* copyStack(SN* originalHead){
  SN* newHead=nullptr;
  if(originalHead!=nullptr){
    newHead=new SN;
    newHead->content=originalHead->content;
    newHead->aNext=copyStack(originalHead->aNext);
  }
  return newHead;
 }
 class CCAccount{ // Credit Card Account
 private:
  std::string cName;// Name of the customer
  double balance;
  SN* aHT;// address of the Head of the stack of Transactions
 public:
  CCAccount(const std::string& = "noNameYet");
  CCAccount(const CCAccount& );
  void operator=(const CCAccount& );
  std::string getName() const;
  double getBalance() const;
  void changeName(const std::string&, const int&);
  // The first argument is new name;
  // The second argument is a password -
  // the programmers without the correct password
  // cannot change the name
  void addCharge(const double&);
  void printStatement() const;// prints all transactions
  ~CCAccount();
 };
 CCAccount::CCAccount(const std::string& s){
  cName=s;
  balance=0.0;
  aHT=nullptr;
 }
 std::string CCAccount::getName() const{
  return cName;
 }
 double CCAccount::getBalance() const{
  return balance;
 }
 void CCAccount::changeName(const std::string& newName, const int & p){
  if(p==123){
    cName=newName;
  }
 }
 void CCAccount::addCharge(const double& x){
  balance=balance+x;
  aHT=push(aHT,x);
 }
 void CCAccount::printStatement() const{
  std::cout<<"The account holder "<<cName<<" has balance ";
  std::cout<<balance<<".\n";
  std::cout<<"The transactions are: ";
  SN* runner;
  runner=aHT;
  while(runner!=nullptr){
    std::cout<<runner->content<<"\t";
    runner=runner->aNext;
  }
  std::cout<<"\n";
 }
 CCAccount::~CCAccount(){
  std::cout<<"Deleting the account for "<<cName<<".\n";
  deleteStack(aHT);
 }
 CCAccount::CCAccount(const CCAccount & copyFrom){
  cName=copyFrom.cName;
  balance=copyFrom.balance;
  aHT=copyStack(copyFrom.aHT);
 }
 void CCAccount::operator=(const CCAccount & assignFrom){
   if(&assignFrom != this){
     deleteStack(aHT);
     cName=assignFrom.cName;
     balance=assignFrom.balance;
     aHT=copyStack(assignFrom.aHT);
   }
 }
 int main(){
  CCAccount customerA("BSimpson");
  customerA.addCharge(18.99);customerA.addCharge(5.23);
  customerA.addCharge(18.29);
  customerA.printStatement();
  int uI;
  std::cout<<"Do you want to add another user? [1=yes, 0=no] ";
  std::cin>>uI;
  if(uI==1){
    CCAccount customerB(customerA);
    customerB.changeName("LisaSimpson",123);
    customerB.addCharge(20.01);customerB.addCharge(19.09);
    customerB.addCharge(79.95);
    customerB.printStatement();customerA.printStatement();
  }

  customerA.printStatement();

  return 0;
 }

5. Move constructor and move assignment

The move constructor and move assignment do not copy the stack from the argument moveFrom. Instead, they include the command

aHT=moveFrom.aHT;
This command steals the address of the stack that corresponds to moveFrom. Then, the attribute aHT of moveFrom has to be changed to nullptr to prevent the destructor of moveFrom from destroying the stack that is passed from moveFrom.

The declarations of the two methods are

 CCAccount(CCAccount &&);
 void operator=(CCAccount &&);
and their implementations are
CCAccount::CCAccount(CCAccount&& moveFrom){
  cName=moveFrom.cName;
  balance=moveFrom.balance;
  aHT=moveFrom.aHT;
  moveFrom.aHT=nullptr;
  moveFrom.balance=0.0;
 }
 void CCAccount::operator=(CCAccount&& moveFrom){
  if(&moveFrom != this){
    cName=moveFrom.cName;
    balance=moveFrom.balance;
    deleteStack(aHT);
    aHT=moveFrom.aHT;
    moveFrom.aHT=nullptr;
    moveFrom.balance=0.0;
  }
}

6. Practice

Problem 1.

Create a class BagOfPoints that represents a collection of points in two-dimensional space. Its private attributes are numPoints, which represents the total number of points, and two sequences sequenceX and sequenceY that are of type double*. The declaration of the class should be

class BagOfPoints{
private:
   double *sequenceX;
    double *sequenceY;
    int numPoints;
public:
    BagOfPoints(const int & =0);
    BagOfPoints(const BagOfPoints &);
    BagOfPoints(BagOfPoints&&);
    void operator=(const BagOfPoints &);
    void operator=(BagOfPoints&&);
    ~BagOfPoints();
    double getX(const int &) const;
    double getY(const int &) const;
    int getLength() const;
    void setTerm(const int &, const double &, const double &);
    void setLength(const int &);
};
  • (a) The methods getX(const int& k) and getY(const int& k) should return the \(x\) and \(y\) coordinates of the \(k\)-th point.
  • (b) The method getLength() should return the attribute numPoints.
  • (c) The method setTerm(const int & i, const double &x, const double &y) should set the coordinates of the \(i\)-th point to x and y.
  • (d) The method setLength(const int &n) should change the length of the sequences to \(n\). Keep in mind that this means that the sequences have to be re-allocated. For example if the old length was \(7\) and the new is \(107\), you may have only allocated the blocks of length \(7\) for the pointers sequenceX and sequenceY. You need to create new memory locations, copy the appropriate values of sequences sequenceX and sequenceY, re-assign the pointers sequenceX and sequenceY to point to the new locations, and delete the old memory to prevent the memory leak.
  • (e) The method bagOfPoints(const int& k); should be the default constructor that sets the number of points to \(k\) and allocates the memory for the sequences sequnceX and sequenceY. The methods BagOfPoints(const BagOfPoints& copyFrom); and void operator=(const BagOfPoints& copyFrom); are copy constructor and copy assignment operator. They should allocate the memory for sequenceX and sequenceY to be the exact size as the corresponding sequences in copyFrom. However, they should be at new locations. The values copyFrom.sequenceX[i] should be copied to sequenceX[i], and analogous should hold for copyFrom.sequenceY[i].
  • (f) The methods BagOfPoints(BagOfPoints&& moveFrom); and void operator=(BagOfPoints&& moveFrom); should be the move constructor and move assignment operator. Instead of allocating new memory for the sequences sequenceX and sequenceY, the move constructor and move assignment operator should instead just position the pointers sequenceX and sequenceY to the addresses of the corresponding pointers of the object moveFrom. However, the object moveFrom should then have its pointers set to nullptr so there is no error when destructor tries to free the memory.
  • (g) The method ~BagOfPoints() should be the destructor and should free the memory occupied by sequences sequenceX and sequenceY

In addition to the class and methods described above, create a function BagOfPoints reflectPoints(const BagOfPoints& bag); that multiplies with \(-1\) every \(x\)-coordinate and every \(y\)-coordinate of every point from the bag. Create a function int main() in which the user first enters the number of points he/she wishes to have in the bag, and then the user inserts the coordinates of these points. Based on the user input create an object bP that corresponds to the bag of points that user has entered. Create an object bPReflected that corresponds to the reflected bag of points obtained from the function reflectPoints. Discuss which constructors and/or assignment operators were called during the execution of your program.