Classes in C++

Programming Workshop 2 (CSCI 1061U)

Faisal Qureshi

Faculty of Science, Ontario Tech University

http://vclab.science.uoit.ca


A Working Example

Our goal is to create a vec2 class that can be used to store the information of a 2-dimensional (2D) 0vector. As seen in the figure below, each 2D vectors has two components: \(x\) and \(y\). We will use this class to illustrates C++ classes in action.

2-dimenstional vectors

Part 1: The Basics

Topics

Implementation

vec2.h

See the vec2.h that defines the vector class.

#include <iostream>

class vec2 {
public:
  double x;
  double y;

public:

  // Constructors
  vec2();
  vec2(double c);
  vec2(double x, double y);
  vec2(const vec2& o); // Copy constructor
  
  // Destructor
  ~vec2();
  
public:
  double length();

  // Assignment operator
  vec2& operator=(const vec2& o); 
};

vec2.cpp

The corresponding vec2.cpp file is

#include "vec2.h"
#include "math.h"

using namespace std;

vec2::vec2() : x(0.0), y(0.0) // This is called the initializer-list.
                              // We can use it to initialize the member data.
{}

vec2::vec2(double c)
{
  this->x = c;
  this->y = c;
}

vec2::vec2(double x, double y)
{
  this->x = x;
  this->y = y;
}

vec2::vec2(const vec2& o) // The copy constructor.
                          // const keyword indicates that
                          // we are not allowed to modify the 
                          // passed reference, which makes sense
                          // it is unwise to modify o when copying it.
{
  x = o.x;
  y = o.y;
}

vec2::~vec2() // This is called when an instance is destroyed
              // 1. Goes out of scope
              // 2. delete is called (in case of dynamic allocations)
{}

vec2& vec2::operator=(const vec2& o) // The assignment operator.
                                     // const keyword indicates that
                                     // we are not allowed to modify the 
                                     // passed reference, which makes sense
                                     // it is unwise to modify o when copying it.
{
  x = o.x;
  y = o.y;
  return *this;     // this is a pointer that points to this instance of vec2.
                    // here, we are return a reference, so we need an object.
                    // we use the de-reference operator * to do so.
}

double vec2::length()
{
  return sqrt(x*x + y*y);
}

main.cpp

main.cpp file below uses this 2D vectors.

#include <iostream>
#include "vec2.h"

using namespace std;

int main()
{
  vec2 a;
  a.x = 1.5;
  a.y = 2.7;

  vec2 b(2.4, 54.4);
  //cout << "length of b = " << b.length() << endl;
  
  cout << "a = (" << a.x << "," << a.y << ")" << endl; 
  cout << "b = (" << b.x << "," << b.y << ")" << endl; 

  vec2* c = new vec2(9.9999);
  cout << "c = (" << c->x << "," << c->y << ")" << endl; 
  delete c;

  b = a; // Using the assignemnt operator
  cout << "b = (" << b.x << "," << b.y << ")" << endl; 
  
  return 0;
}

Compilation

We can compile the above code as follows

$ g++ main.cpp vec2.cpp -o vec2

Part 2: More Than Just Basics

Continued from above

Topics

Implementation

Consider the following vec2.h file that showcases the use of const and friend functions.

It also provides a recipe for writing insertion << and extraction >> operators.

vec2.h

#include <iostream>

class vec2 {
protected:
  double x;
  double y;
  
public:
  vec2();
  vec2(double c);
  vec2(double x, double y);
  vec2(const vec2& o); // Copy constructor
  ~vec2(); // Destructor
  
public:
  double length2() const; // A member const function
                          // to compute the squared length
                          // of this function.  By declaring
                          // this function const, we
                          // promise that this function
                          // will not modify "this" instance, i.e.,
                          // members x and y will remain unchanged.
                          // this allows the following to work
                          //
                          //   vec2 v(1,2);
                          //   const vec2& const_v_ref = v;
                          //   cout << "Squred length = " << const_v_ref.length2() << endl;
                          //
                          // Note that if this function was not
                          // declared const, the following statement
                          // will give a compile-time error:
                          //
                          //   const_v_ref.length2()
  
  friend double length2(const vec2& v); // A friend function, which is not
                                        // not a member of vec2, to compute
                                        // the squred length of this vector.
                                        // Since this function is not a member,
                                        // we need to pass a vec2 as argument.
                                        // Here we pass a const references to
                                        // a vec2.  This is the recommended
                                        // approach to pass objects due to the following
                                        // advantages:
                                        //    1. We are only passing a reference to
                                        //       the object, avoiding a copy.
                                        //    2. Since we don't expect a vec2 to
                                        //       change because its squared length
                                        //       is computed, we pass a const reference.
                                        // Note that we also decrare this function
                                        // a friend.  That allows that vec2 instance
                                        // within this function can access the protected
                                        // members of vec2.
                                        
  const vec2& operator=(const vec2& o) { // Assignment operator
    x = o.x;
    y = o.y;
    return *this;
  }

  // vec2 a, b;
  // cout << a << ' ' << b << endl;
  friend std::ostream& operator<<(std::ostream& stream, const vec2& o) { // Insertor operator
                                                                         // Use to insert data to
                                                                         // ostream objects
    stream << o.x << ' ' << o.y;
    return stream;
  }

  // vec2 a
  // cin >> a;
  friend std::istream& operator>>(std::istream& stream, vec2& o) { // extractor operator
                                                                   // Use to extract data from
                                                                   // istream objects
    stream >> o.x;
    stream >> o.y;
    return stream;
  }
}

vec2.cpp

The corresponding cpp file vec2.cpp is

#include "vec2.h"
#include "math.h"

using namespace std;

vec2::vec2() : x(0.0), y(0.0)
{
}

vec2::vec2(double c)
{
  x = c;
  y = c;
}

vec2::vec2(double x, double y)
{
  this->x = x; // Using this pointer to differentiate
  this->y = y; // between members x and y and
               // function arguments x and y.
}

vec2::vec2(const vec2& o)
{
  x = o.x;
  y = o.y;
}

vec2::~vec2()
{}

double vec2::length2() const
{
  return x*x + y*y;
}

double length2(const vec2& v)
{
  return v.x*v.x + v.y*v.y;
}

main.cpp

And the main.cpp file that you can use to experiment with these files is

#include <iostream>
#include "vec2.h"

using namespace std;


int main()
{
  vec2 a(2,4);
  cout << "a = " << a << endl;
  cout << "length of a = " << a.length2() << endl;
  cout << "length of a = " << length2(a) << endl;

  const vec2& b = a;
  cout << "length of b = " << b.length2() << endl;

  vec2 c;
  c = b;
  cout << "length of c = " << c.length2() << endl;

  vec2 d;
  cout << "d = " << d << endl;
  cout << "Enter d: ";
  cin >> d;
  cout << "d = " << d << endl;
  
  return 0;
}

Compilation

You can compile this program as follows:

$ g++ vec2.cpp main.cpp -o v

References