CtoC++

From SourceWiki
Jump to navigation Jump to search

CtoC++: Upgrading to Object Oriented C

Introduction

This tutorial carries on where StartingC left off.

To get the material, cut and paste the contents of the box below onto your command line.

svn co http://source.ggy.bris.ac.uk/subversion-open/CtoC++/trunk ./CtoC++

In this tutorial we will assume basic linux skills as outlined in Linux1.

Cutting to the Chase: Classes and Encapsulation

So, he we are contemplating C++. We've got to grips with most of the C language in StartingC and it looked alright. Definitely serviceable. What's all the fuss about C++? Well, I believe that most of the fuss is about encapsulation. We saw the benefit of collecting together related variables into structures in C, true? Well, C++ goes further and allows us to collect together not only related variables, but also functions which use those variables too. An instance of a class is called an object and it comes preloaded with all the variables and functions (aka methods) that you'll need when considering said object.

What may have seemed like the relatively small enhancement of adding methods to the encapsulation has, in fact, resulted in a sea-change. No longer are we thinking about a program in terms of the variables and the functions, but instead we're thinking about objects (planets, radios, payrolls and the like) and how they interact with other objects. The whole thing has become far more modular, and so easier to work with. Indeed, this is no accident as object oriented programming (OOP) arose in response to programs written in the functional style getting larger and unwieldy and hard to work with. In short it arose to swap spaghetti for lego.

OK, enough of the spiel, let's get going with an example:

cd CtoC++/examples/example1
make

The first chunk of code to greet you inside class.cc (we'll use .cc to denote C++ source code files) is:

//
// This is a C++ comment line
//

#include <iostream>        // A useful C++ library
#include <cmath>           // The standard C math library

// declare a namespace in which to keep 
// some handy scientific constants
namespace scientific
{
  const double pi            = 3.14159265; // note the use of 'const'
  const double grav_constant = 6.673e-11;  // uinversal graviational constant (m3 kg-1 s-2) 
  const int    sec_per_day   = 86400;      // number of seconds in 24 hours
}

// avail ourselves of a couple of namespaces
// via the 'using' directive
using namespace std;           // allows us to use 'cout', for example
using namespace scientific;

What's new? Well, first up, we see that the comment syntax has changed and that we can use just a leading double forward slash (//) to signal a note from the author. #include is familiar, except that we've dropped the .hs from inside the angle brackets.

The next block is a namespace declaration. The concept of a namespace is common to a number of programming languages and here we're setting one up called scientific and using it to store some handy constants. We can enclose anything we like in a namespace. We access the contents of a namespace via the using directive. In this case we're accessing an intrinsic one called std (standard)--we'll be doing that a lot!--and also our scientific one. The idea behind namespaces is to reduce the risk of a clash of names when programs get large. They're handy.


Next up in the source code is the class declaration (and definition, as it heppens) itself:

class satellite
{
private:

  // Private members of a class class cannot be accessed
  // from outside the class.
  double period;        // time taken to orbit e.g. earth (s) 
  double sma_of_orbit;  // semi-major axis of satellite's orbit (m)

public:

  // Public members of the class are visible to the
  // rest of the program.

  // Method to assign values to private variables. 
  void set(const double prd, const double sma)
  {
    period = prd;
    sma_of_orbit = sma;
  }

  // Method to compute mass of a celestial body
  // given the period of a satellite which orbits
  // it and the semi-major axis of that orbit.
  // See Kepler's laws of planetary motion.
  double mass_of_attractor(void)
  {
    return (4.0 * pow(sma_of_orbit,3) * pow(pi,2)) / (pow(period,2) * grav_constant);
  }

};

You can see that the class called satellite contains some variables and also some methods. The contents of the class is also separated into two sections by the keywords private and public. We've declared our variables to be private (cannot be seen from outside the class) and our methods to be public (are visible from outside). In doing so, we've set up an interface (i.e. the public methods) through which other parts of the program can interact with this class. In this case, the program at large can call set(), providing information about the satellite's orbit as it does so, and also mass_of_attractor() in order to discover the mass of whatever the satellite is orbiting.

The existence of an interface simplifies the ways in which the object interacts with the rest of the program and means that any alterations to the program are much easier to make. For example, any you can make changes to the internals of a class without fear that you will unwittingly break some aspect of the program outside of the interface. Indeed, we could entirely re-write the contents of a (perhaps complex) class and as long as the interface remains unchanged, the rest of the program need never know! This is quite a boon for scientific software, which has a more rapid schedule of alterations that other kinds of software.


Last up is our glue code, or main function:

int main (void)
{
  // Declare an 'instance' of the satellite class,
  // called 'moon'.
  satellite moon;

  cout << "== Welcome to the intro to classes program! ==" << endl << endl;

  // Set some values pertaining to the moon.
  moon.set((27.322*sec_per_day),384399e3);

  // Call a method of the satellite class
  // and report results to the 'stdout' stream.
  cout << "Mass of the Earth (kg) is: "<< moon.mass_of_attractor() << endl;

  return EXIT_SUCCESS;
}

in which we declare in instance of our satellite class, the moon object, call set() and finally mass_of_attractor(), noting the dot (.) operator for accessing members of the class.

The way in which we print to stdout is also different in C++. Here we have used the left shift operator (<<) together with the cout I/O stream and also the endline (endl) operator.

You can run the program--and weigh the Earth!--by typing:

./class.exe

(The eagle-eyed amongst you will note that we have a small error in our calculation of mass. The intrigued amongst the cohort of eagles may be relieved to see that Kepler's law gives the combined mass of the moon and Earth in this case, and that if we subtract off the mass of the moon, we get closer to the actual mass of the Earth--phew!)


Exercises

  • Add a new method to the satellite class to compute the mean orbital speed of the satellite, and perhaps another to compute the satellites speed at various points along it's orbit?
  • Add a whole new class to the program. One for a planet orbiting a star, such as our sun, would be a good one.

More on Methods

Constructors, destructors, overloading


Exercises

  • Method arguments can have defaults attached, e.g. satellite(const double period = 0.0 ...). Rewrite our constructor with arguments, so that we no longer need a default constructor.

Templates

Inheritance

Not multiple inheritance.

The Standard Template Library