Wrox Press C++ Tutorial
We've touched on most of the basic aspects of defining a class, so maybe we should look at how a class might be used to solve a problem. We'll need to keep the problem simple in order to keep this book down to a reasonable number of pages, so we'll consider problems in which we can use an extended version of the Box class.
The implementation of an extended Box class should incorporate the notion of a class interface. What we're going to provide is a tool kit for anyone wanting to work with Box objects, so we need to assemble a set of functions that represents the interface to the world of boxes. Since the interface will represent the only way to deal with Box objects, it needs to be defined to adequately cover the likely things one would want to do with a Box object, and be implemented, as far as possible, in a manner that protects against misuse or accidental errors.
The first question that we need to consider is the nature of the problem we intend to solve and, from that, derive the kind of functionality we need to provide in the class interface.
The principal function of a box is to contain objects of one kind or another so, in a word, our problem is packaging. We'll attempt to provide a class that eases packaging problems in general and then see how it might be used. We'll assume that we'll always be working on packing Box objects into other Box objects since, if you want to pack candy in a box, you could always represent each of the pieces of candy as an idealized Box object. The basic operations that we might want to provide for our Box class include:
Box. This is a fundamental characteristic of a Box object and we have an implementation of this already.Box objects to determine which is the larger. We probably should support a complete set of comparison operators for Box objects. We already have a version of the > operator.Box object with a specified value and vice versa. We also have an implementation of this for the > operator, but we will also need the other comparison operators.Box objects to produce a Box object which will contain both the original objects. Thus, the result will be at least the sum of the volumes, but may be larger. We have a version of this already by overloading the + operator.Box object by an integer (and vice versa) to provide a Box object which will contain a specified number of the original objects. This is effectively designing a carton.Box objects of a given size can be packed in another Box object of a given size. This is effectively division, so we could overload the / operator.Box object after packing it with the maximum number of Box objects of a given size.We had better stop right there! There are undoubtedly other functions that would be very useful but, in the interest of saving trees, we'll consider the set complete, apart from ancillaries such as accessing dimensions, for example.
We really need to consider the degree of error protection that we want to build into the Box class. The basic class that we defined to illustrate various aspects of classes is a starting point, but we should also consider some points a little more deeply. The constructor is a little weak in that it doesn't ensure that we have valid dimensions for a Box, so perhaps the first thing we should do is to ensure we always have valid objects. We could redefine the basic class as follows:
class Box // Class definition at global scope
{
public:
// Constructor definition
Box(double lv=1.0, double bv=1.0, double hv=1.0)
{
lv = lv<=0? 1.0: lv; // Ensure positive
bv = bv<=0? 1.0: bv; // dimensions for
hv = hv<=0? 1.0: hv; // the object
length = lv>bv? lv: bv; // Ensure that
breadth = bv < lv? bv: lv; // length >= breadth
height = hv;
}
// Function to calculate the volume of a box
double Volume() const
{
return length * breadth * height;
}
// Function providing the length of a box
double GetLength() const { return length; }
// Function providing the breadth of a box
double GetBreadth() const { return breadth; }
// Function providing the height of a box
double GetHeight() const { return height; }
private:
double length; // Length of a box in inches
double breadth; // Breadth of a box in inches
double height; // Height of a box in inches
};
Our constructor is now secure, since any dimension that the user of the class tries to set to a negative number or zero will be set to 1 in the constructor. You might also consider displaying a message for a negative or zero dimension, since there's obviously an error when this occurs, and arbitrarily and silently setting a dimension to 1 might not be the best solution.
The default copy constructor is satisfactory for our class, since we have no dynamic memory allocation for data members, and the default assignment operator will also work as we would like. The default destructor also works perfectly well in this case, so we don't need to define it. Perhaps now we should consider comparisons of objects of our class.
We should include support for >, >=, ==, < and <= to operate between two Box objects, as well between a Box object and a value of type double. We should implement these as ordinary global functions, since they don't need to be member functions. We can also write the functions to compare two Box objects in terms of the functions to compare a Box object with a double value, so let's start with the latter. We can repeat the operator>() function that we had before:
// Function for testing if a constant is > a Box object
bool operator>(const double& value, const Box& aBox)
{
return value > aBox.Volume();
}
We can now write the operator<() function in a similar way:
// Function for testing if a constant is < Box object
bool operator<(const double& value, const Box& aBox)
{
return value < aBox.Volume();
}
The implementation of the same operators, but with the arguments reversed, can now be specified using these two functions:
// Function for testing if Box object is > a constant
bool operator>(const Box& aBox, const double& value)
{ return value<aBox; }
// Function for testing if Box object is < a constant
bool operator<(const Box& aBox, const double& value)
{ return value>aBox; }
We just use the appropriate overloaded operator function that we wrote before, with the arguments from the call to the new function switched.
>= and <= will be the same as the first two functions but with the <= operator replacing each use of <, and >= instead of >; there's little point in reproducing them at this stage. The operator==() functions are also very similar:
// Function for testing if constant is == a Box object
bool operator==(const double& value, const Box& aBox)
{
return value == aBox.Volume();
}
// Function for testing if Box object is == a constant
bool operator==(const Box& aBox, const double& value)
{
return value == aBox;
}
We now have a complete set of comparison operators for Box objects. Keep in mind that these will also work with expressions as long as the expressions result in objects of the required type, so we will be able to combine them with the use of other overloaded operators.
Now we come to the question of overloading the operators +, *, /, and %. We will take them in order. The add operation that we already have from Ex7_06.cpp has this prototype:
// Function adding two Box objects
Box operator+(const Box& aBox) const;
Although our original implementation of this isn't an ideal solution, we'll use it to avoid overcomplicating our class. A better version would need to see if the operands had any faces with the same dimensions and join along those faces, but coding that could get a bit messy. Of course, if this were a practical application, a better add operation could be developed later and substituted for the existing version, and any programs written using the original would still run without change. The separation of the interface to a class from its implementation is crucial to good C++ programming.
You'll have noticed that we conveniently forgot the subtraction operator. This is a judicious oversight to avoid the complications inherent in implementing this. If you're really enthusiastic about it, and you think it's a sensible idea, you can give it a try - but you need to decide what to do when the result has a negative volume. If you allow the concept, you need to resolve which box dimension, or dimensions, are to be negative, and how such a box is to be handled in subsequent operations.
The multiply operation is very easy. It represents the process of creating a box to contain n boxes, where n is the multiplier. The simplest solution would be to take the length and breadth of the object to be packed and multiply the height by n to get the new Box object. We'll make it a little cleverer by checking whether or not the multiplier is even and, if it is, stacking the boxes side by side by doubling the breadth value and only multiplying the height value by half of n. This is illustrated here:

Of course, we don't need to check which is the larger of the length and breadth for the new object, since the constructor will sort it out automatically for us. We'll write the version of the operator function, operator*(), as a member function, with the left operand as a Box object:
// Box multiply operator this*n
Box operator*(int n) const
{
if(n%2)
return Box(length, breadth, n*height); // n odd
else
return Box(length,2.0*breadth,(n/2)*height); // n even
}
Here, we use the % operator to determine whether n is even or odd. If n is odd, the value of n%2 is 1 and the if statement is true. If it's even, n%2 is 0 and the statement is false.
We can now use the function we've just written in the implementation of the version with the left operand as an integer. We can write this as a non-member function:
// Box multiply operator n*aBox
Box operator*(int n, const Box aBox)
{
return aBox*n;
}
This version of the multiply operation simply reverses the order of the operands so as to use the previous version of the function directly. That completes the set of combinatorial operators for Box objects that we defined. We can finally look at the two analytical operator functions, operator/() and operator%().
As we've said, the division operation will determine how many Box objects, given by the right operand can be contained in the Box object specified by the left operand. To keep it relatively simple, we'll assume that all the Box objects are packed the right way up, that is, with the height dimensions vertical. We'll also assume that they are all packed the same way round, so that their length dimensions are aligned. Without these assumptions, it can get rather complicated.
The problem will then amount to determining how many of the right-operand objects can be placed in a single layer, and then deciding how many layers we can get inside the left-operand Box.
We'll code this as a member function as follows:
int operator/(const Box& aBox) const
{
// Temporary for number in horizontal plane this way
int tc1 = 0;
// Temporary for number in a plane that way
int tc2 = 0;
tc1 = static_cast<int>(length/aBox.length)* // to fit this
static_cast<int>(breadth/aBox.breadth); // way...
tc2 = static_cast<int>(length/aBox.breadth)* // ... and
static_cast<int>(breadth/aBox.length); // that way
//Return best fit
return static_cast_<int>((height/aBox.height)
*(tc1>tc2? tc1:tc2));
}
This function first determines how many of the right-operand Box objects can fit in a layer with their lengths aligned with the length dimension of the left-operand Box. This is stored in tc1. We then calculate how many can fit in a layer with the lengths of the right-operand Boxes lying in the breadth direction of the left-operand Box. We then multiply the larger of tc1 and tc2 by the number of layers we can pack in, and return that value. This process is illustrated here:

We look at two possibilities: fitting bBox into aBox with the length aligned with that of aBox, and then with the length of bBox aligned with the breadth of aBox. You can see from the illustration that the best packing results from rotating bBox, so that the breadth divides into the length of aBox.
The other analytical operator function, operator%(), for obtaining the free volume in a packed aBox is easier, since we can use the operator we've just written. We can write it as an ordinary global function, since we don't need access to the private members of the class.
// Operator to return the free volume in a packed box
double operator%(const Box& aBox, const Box& bBox)
{
return aBox.Volume() - (aBox/bBox) * bBox.Volume();
}
This computation falls out very easily using existing class functions. The result is the volume of the big box, aBox, minus the volume of the bBox boxes in it. The number of bBox objects packed is given by the expression aBox/bBox, which uses the previous overloaded operator. We multiply this by the volume of bBox objects to get the volume to be subtracted from the volume of the large box, aBox.
That completes our class interface. Clearly, there are many more functions that might be required for a production problem solver but, as an interesting working model demonstrating how we can produce a class for solving a particular kind of problem, it will suffice. Now we should go ahead and try it out on a real problem.
Before we can actually start writing the code to use our Box class and its overloaded operators, the first thing we need to do is to assemble the definition for the class into a coherent whole. We're going to take a rather different approach from what you've seen previously, in that we're going to write multiple files for our project. We're also going to start using the facilities that Visual C++ provides for creating and maintaining code for our classes. This will mean that you do rather less of the work, but it will also mean that the code will be slightly different in places.
Start by creating a new project for a console application called Ex7_08. You'll see the left hand window shown here:

This appears on the ClassView tab, which shows a view of all the classes in a project. The next tab to the right is the FileView tab, which presents the files that go to make up the project. The rightmost tab is the InfoView tab, through which you can access the online documentation. Although there are no classes defined - or anything else for that matter - Visual C++ has already made provision for including some. We can use Visual C++ to create a skeleton for our Box class, and the files that relate to it too. Right click anywhere on
in ClassView, and select New Class... from the pop-up menu that appears. You will then be able to enter the name of the class that we want to create, Box, in the New Class dialog as shown here.

The name of the file that's indicated on the dialog, Box.cpp, will be used to contain the class implementation, which consists of the definitions for the function members of the class. This is the executable code for the class. You can change the name of this file by selecting the Change... button if you want, but Box.cpp looks like a good name for the file in this case. The class definition will be stored in a file called Box.h. This is the standard way of structuring a program. Code which consists of class definitions is stored in files with the extension .h, and code which defines functions is stored in files with the extension .cpp. Usually, each class definition goes in its own .h file, and each class implementation goes in its own .cpp file.
When you click on the OK button in the dialog several things will happen:
The Wizard bar should already be displayed, but if it isn't, right click in the menu area of the window and click on Wizard Bar in the pop-up. The Wizard Bar is used like this:

As you can see, the Wizard Bar provides a range of tools for accessing and modifying your code. You can add classes, you can add members to classes, and you can switch between the definition and the implementation of member functions. You can also get any global entity in your program displayed in the editor window. Of course, you should only use the Wizard Bar when it's convenient to do so - you always have the option of displaying a file and modifying your source code directly in the editor window.
Let's start developing our Box class based on what Visual C++ has provided automatically for us.
If you click on the + to the left of
in the ClassView, the tree will be expanded and you will see that Box is now defined for the project. All the classes in a project are displayed in this tree. We can view the source code supplied for the definition of the class by double clicking the class name in the tree, or by clicking on the button in the Wizard Bar. The code is as follows:
// Box.h: interface for the Box class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_BOX_H__A1FA3EA0_CEFB_11D1_915E_00A0C94515AD__INCLUDED_)
#define AFX_BOX_H__A1FA3EA0_CEFB_11D1_915E_00A0C94515AD__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class Box
{
public:
Box();
virtual ~Box();
};
#endif // !defined(AFX_BOX_H__A1FA3EA0_CEFB_11D1_915E_00A0C94515AD__INCLUDED_)
After the comment lines, there are some preprocessor directives. The first is a test whether the symbol beginning AFX_BOX_H__... has not been defined - #if !defined is a directive that implements an 'if not defined' test, and works rather like an ordinary if statement. If the symbol in the directive doesn't already exist, all the text lines down to the #endif directive will be included, including the #define directive. If the symbol has been defined, the lines down to the #endif directive will be skipped.
The effect of this arrangement is to prevent the code in the file from being included into a file more than once. The first time the code is included, the symbol beginning AFX_BOX_H__... gets defined, thus preventing any further inclusions of the same code. This is pretty much standard procedure in .h files, and you should write all your .h files with this sort of protection built in, so that you avoid the problems that duplicate definitions cause. Visual C++ will always include it automatically in the files for class definitions that it creates. Don't worry that numbers and letters in the symbol are different on your machine; this is done to make absolutely sure the symbol is unique.
The second pair of #if-#endif directives performs a similar function, but one which is only available on more recent compilers - that's what _MSC_VER denotes. If the compiler is sufficiently recent (and Visual C++ 5 most certainly is!), the #pragma once directive is executed, which means that this file doesn't even have to be opened again. You can see that this is faster than opening the file and testing the symbol every time the compiler comes across a #include for it.
Within the code, we have an outline class definition, including the default class constructor and the destructor. The destructor has been declared as virtual, but we'll defer discussion of what that means until the next chapter. You could delete the keyword if you want, as it isn't needed here, but it will do no harm if you leave it in.
Note that the button on the Wizard Bar has changed. Clicking on the button while the class definition is displayed will switch to the .cpp file. In fact, it does better than that, although the full benefit isn't clear while your files are as short as they are at this stage. The button switches you to the position in the .cpp file which contains the definition of the function at the current cursor position, with the function header highlighted. Clicking on it again will toggle back to the class definition.
First, we'll add the private data members length, breadth, and height. Right click on
(in class view) and select Add Member Variable... from the pop-up menu. You can then specify the first data member that we want to add to the class in the dialog.

The variable type appears in the upper edit box, and the access specifier is selected from the three radio buttons at the bottom of the dialog. When you click on the OK button, the variable will be added to the class definition. Repeat the process for the other two class data members. You can then add comments to these directly in the editor window if you wish:
// Box.h: interface for the Box class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_BOX_H__A1FA3EA0_CEFB_11D1_915E_00A0C94515AD__INCLUDED_)
#define AFX_BOX_H__A1FA3EA0_CEFB_11D1_915E_00A0C94515AD__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class Box
{
public:
Box();
virtual ~Box();
private:
double length;
double breadth;
double height;
};
#endif // !defined(AFX_BOX_H__A1FA3EA0_CEFB_11D1_915E_00A0C94515AD__INCLUDED_)
Of course, you're quite free to enter the declarations for these members manually, directly into the code, if you want.
We want to change the declaration of the constructor in the class definition, so modify it to:
Box(double lv=1.0, double bv=1.0, double hv=1.0);
Now we're ready to implement it - modify the constructor definition to:
Box::Box(double lv, double bv, double hv)
{
lv = lv<=0.0? 1.0:lv; // Ensure positive
bv = bv<=0.0? 1.0:bv; // dimensions for
hv = hv<=0.0? 1.0:hv; // the object
length = lv>bv? lv:bv; // Ensure that
breadth = bv<lv? bv:lv; // length >= breadth
height = hv;
}
Remember that the initializers for the parameters to a member function should only appear in the member declaration in the class definition, not in the definition of the function. If you put them in the function definition, your code will not compile. We've seen this code already, so we won't discuss it again. It would be a good idea to save the file at this point by clicking on the Save toolbar button. Get into the habit of saving the file you're editing before you switch to something else.
We need to add all the functions you saw earlier to the Box class. Previously, we defined several function members within the class definition, so that these functions were automatically inline. However, if we just add code to the body of each function definition in the .cpp file, the functions won't be inline, and for those that are very short, such as Volume(), GetLength(), GetBreadth(), and GetHeight(), it really would be much better if they were.
You might think it was simply a question of adding the keyword inline to the function definitions, but the problem here is that inline functions end up not being 'real' functions. Because the code from the body of each function has to be inserted directly at the position it is called, the definitions of the functions need to be available when the file containing calls to the functions is compiled. If they're not, you'll get linker errors and your program will not run. If you want member functions to be inline, you must include the function definitions in the .h file for the class. They can be defined either within the class definition, or immediately following it in the .h file. You should put any global inline functions you need into a .h file, and #include that file into any .cpp file that uses them. We'll put the definitions for the functions we want to make inline in the Box class definition, so modify it to:
class Box
{
public:
// Get dimensions of Box
double GetHeight(void) const { return height; }
double GetBreadth(void) const { return breadth; }
double GetLength(void) const { return length; }
// Calculate volume
double Volume(void) const
{ return length * breadth * height; }
Box(double lv=1.0, double bv=1.0, double hv=1.0);
virtual ~Box();
private:
double height; // Height of a box in inches
double breadth; // Breadth of a box in inches
double length; // Length of a box in inches
};
You could enter the other member functions we need directly in the editor window, but you can also use the Wizard Bar to do it, and the practice will be useful. Click on the down arrow in the Wizard Bar and select Add Member Function... from the pop-up. You can then enter the details of the first function we want to add in the dialog that appears:

Here we've defined the operator+() function as public with a return type of Box. Note the options to declare a function as static or virtual. As you know, a static member function exists independently of any objects of a class. We'll get to virtual functions in Chapter 10. When you click on OK, the declaration for the function will be added to the class definition in the Box.h file, and a skeleton definition for the function will be added to the Box.cpp file.
You need to repeat this process for each of the other member functions of Box, so the class definition will look like this:
class Box
{
public:
// Divide one box into another
int operator/(const Box& aBox) const;
// Multiply a box by an integer
Box operator*(int n) const ;
// Add two boxes
Box operator+(const Box& aBox) const ;
// Get dimensions of Box
double GetHeight(void) const { return height; }
double GetBreadth(void) const { return breadth; }
double GetLength(void) const { return length; }
// Calculate volume
double Volume(void) const
{ return length * breadth * height; }
Box(double lv=1.0, double bv=1.0, double hv=1.0);
virtual ~Box();
private:
double height;
double breadth;
double length;
};
The comments were added manually here, as were the consts that appear after the member functions that do not alter the object in any way. The skeleton implementations of these three functions have been placed in Box.cpp. Switch to the .cpp file and define the functions with the implementations we've already discussed. The .cpp file should then contain the following code (where the highlighted areas indicate the code that you need to add yourself):
// Box.cpp: implementation of the Box class.
//
//////////////////////////////////////////////////////////////////////
#include "Box.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
Box::Box(double lv, double bv, double hv)
{
lv = lv<=0.0? 1.0:lv; // Ensure positive
bv = bv<=0.0? 1.0:bv; // dimensions for
hv = hv<=0.0? 1.0:hv; // the object
length = lv>bv? lv:bv; // Ensure that
breadth = bv<lv? bv:lv; // length >= breadth
height = hv;
}
Box::~Box()
{
}
Box Box::operator +(const Box& aBox) const
{
// New object has larger length and breadth of the two,
// and sum of the two heights
return Box(length>aBox.length? length:aBox.length,
breadth>aBox.breadth? breadth:aBox.breadth,
height+aBox.height);
}
Box Box::operator *(int n) const
{
if(n%2)
return Box(length, breadth, n*height); // n odd
else
return Box(length, 2.0*breadth,(n/2)*height); // n even
}
int Box::operator /(const Box& aBox) const
{
// Temporary for number in horizontal plane this way
int tc1 = 0;
// Temporary for number in a plane that way
int tc2 = 0;
tc1 = static_cast<int>(length/aBox.length)* // to fit this
static_cast<int>(breadth/aBox.breadth); // way...
tc2 = static_cast<int>(length/aBox.breadth)* // ...and that
static_cast<int>(breadth/aBox.length); // way
//Return best fit
return static_cast<int>(height/aBox.height)
*(tc1>tc2? tc1:tc2);
}
The very short functions, particularly those that just return the value of a data member, have their definitions within the class definition so that they are inline. If you take a look at ClassView by clicking on the tab, and then click on the + beside the Box class name, you'll see that all the members of the class are shown.
This completes the Box class, but we still need to define the global functions that implement operators to compare the volume of a Box object with a numerical value.
We need to create a .cpp file containing the definitions for the global functions supporting operations on Box objects. Click on the new text file menu button to open a new text file, enter the following code in the editor window, and save the file as BoxOperators.cpp:
// BoxOperators.cpp
// Box object operations that don't rely on private members
#include "Box.h"
// Function for testing if a constant is > a Box object
int operator>(const double& value, const Box& aBox)
{ return value > aBox.Volume(); }
// Function for testing if a constant is < Box object
int operator<(const double& value, const Box& aBox)
{ return value < aBox.Volume(); }
// Function for testing if Box object is > a constant
int operator>(const Box& aBox, const double& value)
{ return value < aBox; }
// Function for testing if Box object is < a constant
int operator<(const Box& aBox, const double& value)
{ return value > aBox; }
// Function for testing if a constant is >= a Box object
int operator>=(const double& value, const Box& aBox)
{ return value >= aBox.Volume(); }
// Function for testing if a constant is <= Box object
int operator<=(const double& value, const Box& aBox)
{ return value <= aBox.Volume(); }
// Function for testing if Box object is >= a constant
int operator>=(const Box& aBox, const double& value)
{ return value <= aBox; }
// Function for testing if Box object is <= a constant
int operator<=(const Box& aBox, const double& value)
{ return value >= aBox; }
// Function for testing if a constant is == Box object
int operator==(const double& value, const Box& aBox)
{ return value == aBox.Volume(); }
// Function for testing if Box object is == a constant
int operator==(const Box& aBox, const double& value)
{ return value == aBox; }
// Box multiply operator n*aBox
Box operator*(int n, const Box aBox)
{ return aBox * n; }
// Operator to return the free volume in a packed Box
double operator%(const Box& aBox, const Box& bBox)
{ return aBox.Volume() - (aBox/bBox) * bBox.Volume(); }
We have an #include directive for Box.h because the functions refer to the Box class. We can add this file to the project by right clicking in the editor window and selecting the project name Ex7_08 that appears alongside the Insert File into Project menu option in the pop-up. When you have completed this, the Wizard Bar will show the global functions in the right-hand drop-down list box, and you can move between function definitions by selecting from this list. You have seen all these function definitions earlier in the chapter, so we won't discuss their implementations again. When we want to use them in another .cpp file, we'll need to be sure that we declare all the functions so the compiler will recognize them. We can achieve this by putting a set of declarations in a header file:
// BoxOperators.h - Declarations for global box operators
int operator>(const double& value, const Box& aBox);
int operator<(const double& value, const Box& aBox);
int operator>(const Box& aBox, const double& value);
int operator<(const Box& aBox, const double& value);
int operator>=(const double& value, const Box& aBox);
int operator<=(const double& value, const Box& aBox);
int operator>=(const Box& aBox, const double& value);
int operator<=(const Box& aBox, const double& value);
int operator==(const double& value, const Box& aBox);
int operator==(const Box& aBox, const double& value);
Box operator*(int n, const Box aBox);
double operator%(const Box& aBox, const Box& bBox);
Once you've saved this file in the project directory, you can add the file to the project by right-clicking in the editor window and selecting the option from the pop-up menu. This will ensure that the Visual C++ displays the file as part of the project in FileView immediately. If you don't add the file to the project, Visual C++ treats it as an external dependency, which it will not keep track of automatically. Such files will appear in an External Dependencies sub-folder of the files folder in FileView after you compile the program for the first time.
We're now ready to start applying these functions, along with the Box class, to a specific problem in the world of boxes.
Let's suppose that we need to package candies. They are on the big side, real jaw breakers, occupying an envelope 1.5 inches long by 1 inch wide by 1 inch high. We have access to a standard candy box that is 4.5 inches by 7 inches by 2 inches, and we want to know how many candies fit in the box so that we can set the price. We also have a standard carton that is 2 feet 6 inches long, by 18 inches wide and 18 inches deep, and we want to know how many boxes of candy it can hold and how much space we're wasting.
In case the standard candy box isn't a good solution, we would also like to know what custom candy box would be suitable. We know that we can get a good price on boxes with a length from 3 inches to 7 inches, a breadth from 3 inches to 5 inches and a height from 1 inch to 2.5 inches, where each dimension can vary in steps of half an inch. We also know that we need to have at least 30 candies in a box, because this is the minimum quantity consumed by our largest customers at a sitting. Also, the candy box should not have empty space, because the complaints from customers who think they are being cheated goes up. Further, ideally we want to pack the standard carton completely so the candies don't rattle around. We don't want to be too stringent about this, so let's say we have no wasted space if the free space in the packed carton is less than the volume of a single candy box.
With our Box class, the problem becomes almost trivial; the solution is represented by the following main() function. Create a new text file by clicking on the new file button, then type in the code shown here and add the file to the project as Ex7_08.cpp, just as you did with the previous .cpp file:
// Ex7_08.cpp
// A sample packaging problem
#include <iostream>
#include "Box.h"
#include "BoxOperators.h"
using namespace std;
int main(void)
{
Box Candy(1.5, 1.0, 1.0); // Candy definition
Box CandyBox(7.0, 4.5, 2.0); // Candy box definition
Box Carton(30.0, 18.0, 18.0); // Carton definition
// Calculate candies per candy box
int NumCandies = CandyBox/Candy;
// Calculate candy boxes per carton
int NumBoxes = Carton/CandyBox;
// Calculate wasted carton space
double space = Carton%CandyBox;
cout << endl
<< "There are " << NumCandies
<< " candies per candy box"
<< endl
<< "For the standard boxes there are " << NumBoxes
<< " candy boxes per carton " << endl << "with "
<< space << " cubic inches wasted.";
cout << endl << endl
<< "CUSTOM CANDY BOX ANALYSIS (No Waste)";
// Try the whole range of custom candy boxes
for(double length = 3.0; length <= 7.5; length += 0.5)
for(double breadth = 3.0; breadth <= 5.0; breadth += 0.5)
for(double height = 1.0; height <= 2.5; height += 0.5)
{
// Create new box each cycle
Box TryBox(length, breadth, height);
if(Carton%TryBox < TryBox.Volume() &&
TryBox%Candy == 0.0 && TryBox/Candy >= 30)
cout << endl << endl
<< "TryBox L = " << TryBox.GetLength()
<< " B = " << TryBox.GetBreadth()
<< " H = " << TryBox.GetHeight()
<< endl
<< "TryBox contains "
<< TryBox/Candy << " candies"
<< " and a carton contains " << Carton/TryBox
<< " candy boxes.";
}
cout << endl;
return 0;
}
We should first look at how our program is structured. We've divided it into a number of files, which is common when writing in C++. You should be able to see them if you look at the FileView:

The file Ex7_08.cpp contains our function main() and a #include directive for the file BoxOperators.h, which contains the prototypes for the functions in BoxOperators.cpp (which aren't class members). It also has a #include directive for the definition of the class Box in Box.h. So, a C++ console program is usually divided into a number of files that each fall into one of three basic categories:
.h files containing library #include commands, global constants and variables, class definitions and function prototypes - in other words, everything except executable code. They also contain inline function definitions. Where a program has several class definitions, they are usually placed in separate .h files..cpp files containing the executable code for the program, plus #include commands for all the definitions required by the executable code..cpp file containing the function main().
All the .cpp files must be added explicitly to the project for the program. Visual C++ automatically incorporates all the files that are dependencies (the .h files), deducing these from the #include directives in the .cpp files.
The code in our function main() really doesn't need a lot of explanation - it's almost a direct expression of the definition of the problem in words, because the operators in the class interface perform problem-oriented actions on Box objects.
The solution to the question of the use of standard boxes is in the declaration statements, which also compute the answers we require as initializing values. We then output these values with some explanatory comments.
The second part of the problem is solved using the three nested for loops iterating over the possible ranges of length, breadth and height, so that we evaluate all possible combinations. We could output them all as well, but since this would involve 200 combinations, of which we might only be interested in a few, we have an if which defines the options that we're actually interested in. The if expression is only true if there's no space wasted in the carton and the current trial candy box has no wasted space and it contains at least 30 candies.
Here's the output from this program:

We have a duplicate solution due to the fact that, in the nested loop, we evaluate boxes that have a length of 5 and a breadth of 4.5, as well as boxes that have a length of 4.5 and a breadth of 5. Because our class constructor ensures that the length is not less than the breadth, these two are identical. We could include some additional logic to avoid presenting duplicates - you could treat it as a small exercise if you like.