# Designing a Class¶

Read time: 29 minutes (7369 words)

When we start off building a blueprint for a new kind of object, we have some basic structure to construct. In keeping with the “baby steps” approach to building code, let’s set up a simple program that exercises our new object.

Here is a simple main.cpp program that does not do much:

main.cpp
  1 2 3 4 5 6 7 8 9 10 11 12 13 //Copyright 2019 Roie R. Black #include #include "PoolBall.h" int main(void) { PoolBall ball; std::cout << "Testing the poolball class" << std::endl; std::cout << "We have a ball of radius " << ball.radius << std::endl; } 

Hopefully, you are certain this will work.

Here is a Makefile that will build our project. This pne is set up so you can tweak it to work on any system.

Makefile
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 TARGET = poolballs EXT = PRE = ./ #PRE = #EXT = .exe RM = rm -f #RM = del SRCS = $(wildcard *.cpp) OBJS =$(SRCS:.cpp=.o) .PHONY: all all: $(TARGET)$(EXT) $(TARGET)$(EXT): $(OBJS) g++ -o$@ $^ %.o: %.cpp g++ -c -std=c++11 -o$@ $< .PHONY: run run:$(TARGET)$(EXT)$(PRE)$^ .PHONY: clean clean:$(RM) $(TARGET)$(EXT) $(OBJS)  This program obviously will not run until we set up our new class! ## Setting up a Ball¶ We are going to design a simple class that manages pool balls. As a start, all we need to do is set up the class header file and set up one critical fact about the pool ball: define the radius of the ball! Since, ultimately, we are going to be generating some graphics code to display the ball, I will make the radius an integer variable: PoolBall.h  1 2 3 4 5 6 7 // Copyright 2019 Roie R. Black #pragma once class PoolBall { public: int radius = 5; };  If we now modify our main program code, we can see that this new data type can be used to create a ball object, and display a fact about it: main.cpp   1 2 3 4 5 6 7 8 9 10 11 12 13 //Copyright 2019 Roie R. Black #include #include "PoolBall.h" int main(void) { PoolBall ball; std::cout << "Testing the poolball class" << std::endl; std::cout << "We have a ball of radius " << ball.radius << std::endl; }  And, here is our output: $ make clean
rm -f poolballs main.o

\$ make run
g++ -c -std=c++11 -o main.o main.cpp
g++ -o poolballs main.o
./poolballs
Testing the poolball class


All we have done so far is to set up C++ so it can create a container to hold information about each PoolBall object we create. The actual creation is done in a data declaration statement, identical to those you have used to set up standard data types in your code.

Notice the new notation we use to get at that radius value. The object name followed by a dot and another name accesses attributes within the object container. In this example, we are allowing the outside world to access this value. Here is the result of that:

#include <iostream>

#include "poolball.h"

int main(int argc, char ** argv) {
PoolBall ball;

std::cout << "Testing the poolball class" << std::endl;
std::cout << "We have a new ball of radius " << ball.radius << std::endl;
}


Now, we see this:

Testing the poolball class
We have a new ball of radius 10


If this is not what you want, you can keep this from happening by restricting access to this particular attribute. However, in doing so, we also prevent getting access to the radius of the ball, unless we provide a method that can access that attribute. That needs a few changes.

Here is the change we need in our header file:

#ifndef POOLBALL_H
#define POOLBALL_H

class PoolBall {
private:
int radius = 5;
public:
};

#endif


Now, we have set up a method that can get at the radius, but no one outside of the object itself can modify that attribute.

To make this work, we need to add code to implement the new behavoir we have added to this class:

#include "PoolBall.h"

}


Notice how we name this routine. The name of the class followed by two colons, and then the name of the method itself. Formally, the class name sets up something called a namespace (sound familiar?) that hides away all variables you use in code for this class. You access things through the objects, not directly!

Here is how we access the radius now:

#include <iostream>

#include "poolball.h"

int main(int argc, char ** argv) {
PoolBall ball;

std::cout << "Testing the poolball class" << std::endl;
std::cout << "We have a new ball of radius " << ball.getRadius() << std::endl;
}


Attempting to access the attribute directly will not even compile!

poolball.h: In function 'int main(int, char**)':
poolball.h:6:22: error: 'int PoolBall::radius' is private
main.cpp:10:10: error: within this context
^
In file included from main.cpp:4:0:
poolball.h:6:22: error: 'int PoolBall::radius' is private
int radius = 5;


## Constructors¶

Although we did not build one, C++ requires a special method called a constructor that tells it how to set up the new objects with this blueprint. If we do not provide one, the compiler will generate a default one for us, as it did in this case. The default routine allows no parameters, but you could write one if you had special work to do to get your object ready to go. We do not need that here.

Normally, you might want to set things up yourself with a bit more control, so you add a constructor to your class. The name of the constructor is the same as the name of the class iteslf!

#ifndef POOLBALL_H
#define POOLBALL_H

class PoolBall {
private:
int radius = 5;
public:
void PoolBall(void);        // constructor
void PoolBall(int radius);  // constructor with initial radius
int getRadius(void);        // private attrbiute accessor
};

#endif


Since we have already see that the default constructor works fine, there is no need to add one with no parameters. But, we might like to set up new balls with some other radius. In this case, we added a constructor that takes a single parameter which will be our new radius!

If we add our own constructor, the compiler will not build a default one for us, se we need to add our own for that first object. We do not need any code since there is nothing to do, so the implementation will be empty.

Here are the changes we need in the implementation file:

void PoolBall::PoolBall(int radius) {
}


Do not let that last line confuse you. The this->radius notation on the left hand side of the assignment statement refers to the object attribute, the radius on the right hand side refers to the parameter. We need to tell the compiler explicitly which name is which using that funny this notation.

Constructors can be added to do different things when new objects are created. The only rule is that the signature, formally, the return type of the method, the method name, and the number and type of all parameters must be unique. You are doing something called overloading a name, and the compiler needs to be able to figure out which name you are referring to when there are more than one available.

## Adding more Attributes¶

Our PoolBall class is not very useful for the intended application. We need to be able to tell our graphics engine where the ball is at any moment in time, and we need a way to track the velocity of the ball as it moves. We will use simple variables for all of these, and add code to set up a ball with an initial state. Here is a possibility:

#pragma once

class PoolBall {
private:
int radius = 5;
int xPos, yPos, xVel, yVel;
public:
PoolBall(void);
PoolBall(int radius, int xPos, int yPos, int xVel, int yVel);
};


As we add code, we should be asking questions about what we have, and whether it still makes sense. Since we have added new attributes to our object, we must make sure we set things up properly. Does it make sense to set up a new ball and not specify where it is to ba placed on our table, and should we give it an initial velocity? The answers to these (and other) questions will cause you to alter your code. Personally, I do not like the first two constructors, so I would probably delete them and require a complete setup of each ball I want to have in my simulation. YMMV!

## Inheritance¶

This is a bit of an advanced topic, but if we keep it simple, I think you can see how useful this can be.

Often one kind of object is similar to another kind of object you might work on. Suppose you have two classes and you discover that they both have attributes in common, but one of those classes has a few extra attributes. We can create a top lever class with the shared attributes, then “specialize” that lower level class by adding the additional attributes. We are setting up a “parent->child” kind of relationship.

Of course, this makes no sense unless the behaviours of the two classes are similar as well.

In examining our example class so far, we have focused on a single shape, a “ball” that shape has several attributes that define where the ball is located on the screen, and how fast it is moving. It also has attributes that describe the ball itself.

Now suppose we want to add another object, this one a rectangle to our program. Do we start from scratch and build an entirely new class?

We could, but let’s consider which attributes (and methods) we have designed that would need to also be in the new Rectangle class:

We still need data to tell us where some reference point in our shape is located. For the ball, the center of the object made sense. For a rectangle, it might be the center, but it also might be one corner. Instead of a radius, we need a width and height. The box will also have velocity attributes.

Here is a new class that collect the common attributes we want both objects to share:

#pragma once

class Shape {
public:
void setX(int x);
void setY(int y)
protected:
int Xpos;
int Ypos
};


Notice that we have declared the shared attributes as “protected”, not “private”. This will allow classes that inherit these attributes to use those attributes as though they were private.

Here is how we create our Ball class, not inheriting from the new parent class:

#pragme once

class Ball: public Shape {

public: