Friday, July 18, 2014

C++: Polymorphism Example with Declarations and Definitions Separated

If you use Eclipse CDT[1] to develop C++ programs, you will get both .cpp and .h files when you create a new class.  Taking "Virtual Member" example from [2], we demonstrate what it will look like after separating declarations from definitions.


Monolithic Module


On the Internet, most of examples are given similar to the one below.  It's OK to put everything in one file and test it in a student's project.  More realistically, all large applications are modularized.  In C++ programming, you should separate objects into modules and object's declarations and definitions into header and cpp files.

// virtual members
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b; }
    virtual int area ()
      { return 0; }
};

class Rectangle: public Polygon {
  public:
    int area ()
      { return width * height; }
};

class Triangle: public Polygon {
  public:
    int area ()
      { return (width * height / 2); }
};

int main () {
  Rectangle rect;
  Triangle trgl;
  Polygon poly;
  Polygon * ppoly1 = &rect;
  Polygon * ppoly2 = &trgl;
  Polygon * ppoly3 = &poly;
  ppoly1->set_values (4,5);
  ppoly2->set_values (4,5);
  ppoly3->set_values (4,5);
  cout << ppoly1->area() << '\n';
  cout << ppoly2->area() << '\n';
  cout << ppoly3->area() << '\n';
  return 0;
}

One Definition Rule (ODR)


Someone has posted this question on Stackoverflow:
Is is a good practice to put the declaration of C++ classes into the header file?
Here is the answer:[3]
C++'s compilation model dates back to the days of C, and so its method of importing data from one source file into another is comparatively primitive. The #include directive literally copies the contents of the file you're including into the source file, then treats the result as though it was the file you had written all along. You need to be careful about this because of a C++ policy called the one definition rule (ODR) which states, unsurprisingly, that every function and class should have at most one definition. This means that if you declare a class somewhere, all of that class's member functions should be either not defined at all or defined exactly once in exactly one file. There are some exceptions (I'll get to them in a minute), but for now just treat this rule as if it's a hard-and-fast, no-exceptions rule. 
When you separate an object's declarations from its definitions, here is what will be generated by CDT in the header file:


#ifndef POLYGON_H_
#define POLYGON_H_

class Polygon {
  <snipped>
  ...
};

#endif /* POLYGON_H_ */

Using the above convention, you can safeguard your codes to include any declaration only once directly or indirectly.

Sample Codes


Without much ado, here are the sample codes after the modularization and the separation of declarations and definitions:

Polygon.h


#ifndef POLYGON_H_
#define POLYGON_H_

class Polygon {
protected:
    int width, height;
public:
 Polygon();
 virtual ~Polygon();
 void set_values (int a, int b);
 virtual int area ();
};

#endif /* POLYGON_H_ */

Polygon.cpp


#include "Polygon.h"

Polygon::Polygon() {
  // TODO Auto-generated constructor stub

}

Polygon::~Polygon() {
  // TODO Auto-generated destructor stub
}


void Polygon::set_values (int a, int b) {
  width=a; height=b; 
}

int Polygon::area (){ 
  return 0; 
}

Rectangle.h


#ifndef RECTANGLE_H_
#define RECTANGLE_H_

#include "Polygon.h"

class Rectangle: public Polygon {
public:
 Rectangle();
 virtual ~Rectangle();
 virtual int area ();
};

#endif /* RECTANGLE_H_ */
 

Rectangle.cpp


#include "Rectangle.h"

Rectangle::Rectangle() {
  // TODO Auto-generated constructor stub

}

Rectangle::~Rectangle() {
  // TODO Auto-generated destructor stub
}

int Rectangle::area ()  { 
  return width * height; 
}
 

Triangle.h


#ifndef TRIANGLE_H_
#define TRIANGLE_H_

#include "Polygon.h"

class Triangle: public Polygon {
public:
 Triangle();
 virtual ~Triangle();
 virtual int area ();
};

#endif /* TRIANGLE_H_ */
 

Triangle.cpp


#include "Triangle.h"

Triangle::Triangle() {
  // TODO Auto-generated constructor stub

}

Triangle::~Triangle() {
  // TODO Auto-generated destructor stub
}

int Triangle::area(){
  return (height * width / 2);
}
  

VirtualMember.cpp

 

// virtual members
#include
#include "Polygon.h"
#include "Rectangle.h"
#include "Triangle.h"

using namespace std;

class VirtualMember {
    // TODO Auto-generated constructor stub

};



int main () {
  Rectangle rect;
  Triangle trgl;
  Polygon poly;
  Polygon * ppoly1 = &rect;
  Polygon * ppoly2 = &trgl;
  Polygon * ppoly3 = &poly;
  ppoly1->set_values (4,5);
  ppoly2->set_values (4,5);
  ppoly3->set_values (4,5);
  cout << ppoly1->area() << '\n';
  cout << ppoly2->area() << '\n';
  cout << ppoly3->area() << '\n';
  return 0;
}


References

    1. Eclipse CDT
    2. Polymorphism in C++ 
    3. Is is a good practice to put the declaration of C++ classes into the header file? 

    No comments: