Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning Visual C++ 2005 (2006) [eng]-1

.pdf
Скачиваний:
108
Добавлен:
16.08.2013
Размер:
18.66 Mб
Скачать

More on Classes

You can specify the return type to be double by selecting from the drop-down list, but you could equally well type it in. Obviously, for a type that does not appear in the list, you would just enter it from the keyboard. Selecting the inline checkbox ensures that GetHeight() will be created as an inline function. Note the other options to declare a function as static, virtual, or pure. As you know, a static member function exists independently of any objects of a class. We’ll get to virtual and pure virtual functions in Chapter 9. The GetHeight() function has no parameters so nothing further needs to be added. Clicking the OK button will add the function definition to the class definition in Box.h. If you repeat this process for the GetWidth(), GetLength(), and Volume() member functions, the CBox class definition in Box.h will look like this:

#pragma once

class CBox

{

public:

CBox(double lv = 1.0, double wv = 1.0, double hv = 1.0); ~CBox(void);

private:

//Length of a box in inches double m_Length;

//Width of a box in inches double m_Width;

//Height of a box in inches double m_Height;

public:

double GetHeight(void)

{

return 0;

}

double GetWidth(void)

{

return 0;

}

double CBox::GetLength(void)

{

return 0;

}

// Calculate the volume of a box double Volume(void)

{

return 0;

}

};

An additional public section has been added to the class definition that contains the inline function definitions. You need to modify each of the definitions to provide the correct return value and to declare the functions to be const. For example, the code for GetHeight() function should be changed to:

double GetHeight(void) const

{

return m_Height;

}

449

Chapter 8

You can change the definitions of the GetWidth() and GetLength() functions in a similar way. The Volume() function definition should be changed to:

double Volume(void) const

{

return m_Length*m_Width*m_Height;

}

You could enter the other non-inline member functions directly in the editor pane that shows the code, but of course you can also use the Add Member Function wizard to do it, and the practice will be useful. Right-click CBox in the Class View tab and select the Add/Add Function menu item from the context menu, as before. You can then enter the details of the first function you want to add in the dialog that appears, as Figure 8-12 shows.

Figure 8-12

Here I have defined the operator+() function as public with a return type of CBox. The parameter type and name have also been entered in the appropriate fields. You must click the Add button to register the parameter as being in the parameter list before clicking the Finish button. This also updates the function signature shown at the bottom of the Add Member Function Wizard dialog box. You could then enter details of another parameter if there was more than one and click Add once again to add it. I have also entered a comment in the dialog box and the wizard will insert this in both Box.h and Box.cpp. When you click on Finish, the declaration for the function is added to the class definition in the Box.h file, and a skeleton definition for the function is added to the Box.cpp file. The function needs to be declared as const so you must add this keyword to the declaration of the operator+() function within the class definition, and to the definition of the function on Box.cpp. You must also add the code in the body of the function, like this:

450

More on Classes

CBox CBox::operator +(const CBox& aBox) const

{

//New object has larger length and width of the two,

//and sum of the two heights

return CBox(m_Length > aBox.m_Length ? m_Length : aBox.m_Length, m_Width > aBox.m_Width ? m_Width : aBox.m_Width, m_Height + aBox.m_Height);

}

You need to repeat this process for the operator*() and operator/() functions that you saw earlier. When you have completed this, the class definition in Box.h looks something like this:

#pragma once

class CBox

{

public:

CBox(double lv = 1.0, double wv = 1.0, double hv = 1.0); ~CBox(void);

private:

//Length of a box in inches double m_Length;

//Width of a box in inches double m_Width;

//Height of a box in inches double m_Height;

public:

double GetHeight(void) const

{

return m_Height;

}

public:

double GetWidth(void) const

{

return m_Width;

}

public:

double GetLength(void) const

{

return m_Length;

}

public:

double Volume(void) const

{

return m_Length*m_Width*m_Height;

}

451

Chapter 8

public:

// Overloaded addition operator

CBox operator+(const CBox& aBox) const; public:

// Multiply a box by an integer CBox operator*(int n) const;

public:

// Divide one box into another

int operator/(const CBox& aBox) const;

};

You can edit or rearrange the code in any way that you want(as long as it’s still correct, of course. I have added a few empty lines to make the code a bit more readable.

The contents of the Box.cpp file ultimately look something like this:

#include “.\box.h”

CBox::CBox(double lv, double wv, double hv)

{

lv = lv <= 0.0 ? 1.0 : lv; wv = wv <= 0.0 ? 1.0 : wv; hv = hv <= 0.0 ? 1.0 : hv;

//Ensure positive

//dimensions for

//the object

m_Length = lv>wv ? lv : wv;

// Ensure that

m_Width = wv<lv ? wv : lv;

// length >= width

m_Height = hv;

 

}

CBox::~CBox(void)

{

}

// Overloaded addition operator

CBox CBox::operator+(const CBox& aBox) const

{

//New object has larger length and width of the two,

//and sum of the two heights

return CBox(m_Length > aBox.m_Length ? m_Length : aBox.m_Length, m_Width > aBox.m_Width ? m_Width : aBox.m_Width, m_Height + aBox.m_Height);

}

// Multiply a box by an integer CBox CBox::operator*(int n) const

{

if(n%2)

 

return CBox(m_Length, m_Width, n*m_Height);

// n odd

else

 

return CBox(m_Length, 2.0*m_Width, (n/2)*m_Height);

// n even

452

More on Classes

}

// Divide one box into another

int CBox::operator/(const CBox& 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>((m_Length/aBox.m_Length))*

 

static_cast<int>((m_Width/aBox.m_Width));

// to fit this way

tc2 = static_cast<int>((m_Length/aBox.m_Width))*

 

static_cast<int>((m_Width/aBox.m_Length));

// and that way

//Return best fit

return static_cast<int>((m_Height/aBox.m_Height))*(tc1>tc2 ? tc1 : tc2);

}

The shaded lines are those that you should have modified or added manually.

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 Class View by clicking the tab and then click the + beside the CBox class name, you’ll see that all the members of the class are shown in the lower pane.

This completes the CBox class, but you still need to define the global functions that implement operators to compare the volume of a CBox object with a numerical value.

Adding Global Functions

You need to create a .cpp file that will contain the definitions for the global functions supporting operations on CBox objects. The file also needs to be part of the project. Click the Solution Explorer tab to display it (you currently will have the Class View tab displayed) and right-click the Source Files folder. Select Add > New Item from the context menu to display the dialog box. Choose the category as Code and the template as C++ File (.cpp) in the right pane of the dialog box and enter the file name as BoxOperators.

You can now enter the following code in the Editor pane:

//BoxOperators.cpp

//CBox object operations that don’t need to access private members #include “Box.h”

//Function for testing if a constant is > a CBox object

bool operator>(const double& value, const CBox& aBox) { return value > aBox.Volume(); }

//Function for testing if a constant is < CBox object bool operator<(const double& value, const CBox& aBox) { return value < aBox.Volume(); }

//Function for testing if CBox object is > a constant

453

Chapter 8

bool operator>(const CBox& aBox, const double& value) { return value < aBox; }

//Function for testing if CBox object is < a constant bool operator<( const CBox& aBox, const double& value) { return value > aBox; }

//Function for testing if a constant is >= a CBox object bool operator>=(const double& value, const CBox& aBox)

{ return value >= aBox.Volume(); }

//Function for testing if a constant is <= CBox object bool operator<=(const double& value, const CBox& aBox) { return value <= aBox.Volume(); }

//Function for testing if CBox object is >= a constant bool operator>=( const CBox& aBox, const double& value) { return value <= aBox; }

//Function for testing if CBox object is <= a constant bool operator<=( const CBox& aBox, const double& value) { return value >= aBox; }

//Function for testing if a constant is == CBox object bool operator==(const double& value, const CBox& aBox) { return value == aBox.Volume(); }

//Function for testing if CBox object is == a constant bool operator==(const CBox& aBox, const double& value) { return value == aBox; }

//CBox multiply operator n*aBox

CBox operator*(int n, const CBox& aBox) { return aBox * n; }

// Operator to return the free volume in a packed CBox double operator%( const CBox& aBox, const CBox& bBox)

{ return aBox.Volume() - (aBox / bBox) * bBox.Volume(); }

You have a #include directive for Box.h because the functions refer to the CBox class. Save the file. When you have completed this, you can select the Class View tab. The Class View tab now includes a Global Functions and Variables folder that contain all the functions you just added.

You have seen definitions for all these functions earlier in the chapter, so I won’t discuss their implementations again. When you want to use any of these functions in another .cpp file, you’ll need to be sure that you declare all the functions that you use so the compiler will recognize them. You can achieve this by putting a set of declarations in a header file. Switch back to the Solution Explorer pane once more and right-click the Header Files folder name. Select Add > New Item from the context menu to display the dialog box, but this time select category as Code, the template asHeader File(.h), and enter the name as BoxOperators. After clicking the Add button an empty header file is added to the project, and you can add the following code in the Editor window:

454

More on Classes

// BoxOperators.h - Declarations for global box operators #pragma once

bool operator>(const double& value, const CBox& aBox); bool operator<(const double& value, const CBox& aBox); bool operator>(const CBox& aBox, const double& value); bool operator<(const CBox& aBox, const double& value); bool operator>=(const double& value, const CBox& aBox); bool operator<=(const double& value, const CBox& aBox); bool operator>=(const CBox& aBox, const double& value); bool operator<=(const CBox& aBox, const double& value); bool operator==(const double& value, const CBox& aBox); bool operator==(const CBox& aBox, const double& value); CBox operator*(int n, const CBox aBox);

double operator%(const CBox& aBox, const CBox& bBox);

The #pragma once directive ensures that the contents of the file are not included more than once in a build. You just need to add an #include directive for BoxOperators.h to any source file that makes use of any of these functions.

You’re now ready to start applying these functions, along with the CBox class, to a specific problem in the world of boxes.

Using Our CBox Class

Suppose that you are packaging candies. The candies are on the big side, real jaw breakers, occupying an envelope 1.5 inches long by 1 inch wide by 1 inch high. You have access to a standard candy box that is 4.5 inches by 7 inches by 2 inches, and you want to know how many candies will fit in the box so that you can set the price. You also have a standard carton that is 2 feet 6 inches long, by 18 inches wide and 18 inches deep, and you want to know how many boxes of candy it can hold and how much space you’re wasting when it has been filled.

In case the standard candy box isn’t a good solution, you would also like to know what custom candy box would be suitable. You know that you can get a good price on boxes with a length from 3 inches to 7 inches, a width 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. You also know that you need to have at least 30 candies in a box, because this is the minimum quantity consumed by your 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 you want to pack the standard carton completely so the candies don’t rattle around. You don’t want to be too stringent about this otherwise packing could become difficult, so let’s say you have no wasted space if the free space in the packed carton is less than the volume of a single candy box.

With the CBox class, the problem becomes almost trivial; the solution is represented by the following main() function. Add a new C++ source file, Ex8_08.cpp, to the project through the context menu you get when you right-click Source Files in the Solution Explorer pane, as you’ve done before. You can then type in the code shown here:

455

Chapter 8

// Ex8_08.cpp

 

// A sample packaging problem

 

#include <iostream>

 

#include “Box.h”

 

#include “BoxOperators.h”

 

using std::cout;

 

using std::endl;

 

int main()

 

{

 

CBox candy(1.5, 1.0, 1.0);

// Candy definition

CBox candyBox(7.0, 4.5, 2.0);

// Candy box definition

CBox carton(30.0, 18.0, 18.0);

// Carton definition

// Calculate candies per candy box

 

int numCandies = candyBox/candy;

 

// Calculate candy boxes per carton

 

int numCboxes = 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 “ << numCboxes

<<“ 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 width = 3.0 ; width <= 5.0 ; width += 0.5)

for(double height = 1.0 ; height <= 2.5 ; height += 0.5)

{

// Create new box each cycle

CBox tryBox(length, width, height);

if(carton%tryBox < tryBox.Volume() &&

tryBox % candy == 0.0 && tryBox/candy >= 30) cout << endl << endl

<<“Trial Box L = “ << tryBox.GetLength()

<<“ W = “ << tryBox.GetWidth()

<<“ H = “ << tryBox.GetHeight()

<<endl

<<“Trial Box contains “ << tryBox / candy << “ candies”

<<“ and a carton contains “ << carton / tryBox

<<“ candy boxes.”;

}

cout << endl; return 0;

}

456

More on Classes

Let’s first look at how the program is structured. You have divided it into a number of files, which is common when writing in C++. You will be able to see them if you look at the Solution Explorer tab, which looks as shown in Figure 8-13.

Figure 8-13

The file Ex8_08.cpp contains the main()function and a #include directive for the file BoxOperators.h that contains the prototypes for the functions in BoxOperators.cpp (which aren’t class members). It also has an #include directive for the definition of the class CBox in Box.h. A C++ console program is usually divided into a number of files that will each fall into one of three basic categories:

1.

2.

3.

.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 often 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.

Another .cpp file containing the function main().

The code in our main()function 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 problemoriented actions on CBox 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. You then output these values with some explanatory comments.

457

Chapter 8

The second part of the problem is solved using the three nested for loops iterating over the possible ranges of m_Length, m_Width and m_Height so that you evaluate all possible combinations. You could output them all as well, but because this would involve 200 combinations, of which you might only be interested in a few, you have an if statement that identifies the options that you’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:

There are 42 candies per candy box

For the standard boxes there are 144 candy boxes per carton with 648 cubic inches wasted.

CUSTOM CANDY BOX ANALYSIS (No Waste)

Trial Box L = 5 W = 4.5 H = 2

Trial Box contains 30 candies and a carton contains 216 candy boxes.

Trial Box L = 5 W = 4.5 H = 2

Trial Box contains 30 candies and a carton contains 216 candy boxes.

Trial Box L = 6 W = 4.5 H = 2

Trial Box contains 36 candies and a carton contains 180 candy boxes.

Trial Box L = 6 W = 5 H = 2

Trial Box contains 40 candies and a carton contains 162 candy boxes.

Trial Box L = 7.5 W = 3 H = 2

Trial Box contains 30 candies and a carton contains 216 candy boxes.

You have a duplicate solution due to the fact that, in the nested loop, you evaluate boxes that have a length of 5 and a width of 4.5, as well as boxes that have a length of 4.5 and a width of 5. Because the CBox class constructor ensures that the length is not less than the width, these two are identical. You could include some additional logic to avoid presenting duplicates, but it hardly seems worth the effort. You could treat it as a small exercise if you like.

Organizing Your Program Code

In this last example, you distributed the code among several files for the first time. Not only is this common practice with C++ applications generally, but with Windows programming it is essential. The sheer volume of code involved in even the simplest program necessitates dividing it into workable chunks.

As discussed in the previous section, there are basically two kinds of source code file in a C++ program,

.h files and .cpp files. This is illustrated in Figure 8-14.

There’s the executable code that corresponds to the definitions of the functions that make up the program. Also, there are definitions of various kinds that are necessary for the executable code to compile correctly. These are global constants and variables, data types that include classes, structures, and unions, and function prototypes. The executable source code is stored in files with the extension .cpp, and the definitions are stored in files with the extension .h.

458