1. Trang chủ
  2. » Công Nghệ Thông Tin

Ivor Horton’s Beginning Visual C++ 2005 phần 5 pot

122 328 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 122
Dung lượng 1,61 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

}// Divide one box into anotherint CBox::operator/const CBox& aBox const{// Temporary for number in horizontal plane this wayint tc1 = 0; // Temporary for number in a plane that wayint t

Trang 1

definition, your code will not compile You’ve seen this code already, so I won’t discuss it again It would

be a good idea to save the file at this point by clicking the Savetoolbar button Get into the habit of ing the file you’re editing before you switch to something else If you need to edit the constructor again,you can get to it easily by either double-clicking its entry in the lower pane on the Class View tab or byselecting it from the right drop-down menu above the pane displaying the code

sav-You can also get to a member function’s definition in a cpp file or to its declaration in a h file directly byright-clicking its name in the Class View pane and selecting the appropriate item from the context menuthat appears

Adding Function Members

You need to add all the functions you saw earlier to the CBoxclass Previously, you defined several tion members within the class definition, so that these functions were automatically inline You canachieve the same result by entering the code in the class definition for these functions manually, or youcan use the Add Member Function wizard

func-You might think that you can define each inline function in the cppfile, and add the keyword inline

to the function definitions, but the problem here is that inline functions end up not being “‘real” tions Because the code from the body of each function has to be inserted directly at the position it iscalled, the definitions of the functions need to be available when the file containing calls to the functions

func-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 .hfile for the class They can bedefined either within the class definition, or immediately following it in the hfile You should put anyglobal inline functions you need into a hfile and #includethat file into any cppfile that uses them

To add the GetHeight()function as inline, right-click on CBoxon the Class View tab and select Add >Add Functionfrom the context menu You then can enter the data defining the function in the dialogthat is displayed, as Figure 8-11 shows

Trang 2

You can specify the return type to be doubleby selecting from the drop-down list, but you couldequally well type it in Obviously, for a type that does not appear in the list, you would just enter it fromthe keyboard Selecting the inlinecheckbox ensures that GetHeight()will be created as an inlinefunction Note the other options to declare a function as static, virtual, or pure As you know, astaticmember function exists independently of any objects of a class We’ll get to virtual and pure vir-tual functions in Chapter 9 The GetHeight()function has no parameters so nothing further needs to

be added Clicking the OKbutton will add the function definition to the class definition in Box.h If yourepeat this process for the GetWidth(), GetLength(), and Volume()member functions, the CBoxclass definition in Box.hwill look like this:

#pragma onceclass CBox{

return 0;

}double GetWidth(void){

return 0;

}double CBox::GetLength(void){

return 0;

}// Calculate the volume of a boxdouble Volume(void)

{return 0;

}};

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

defi-double GetHeight(void) const{

return m_Height;

Trang 3

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

double Volume(void) const

Figure 8-12

Here I have defined the operator+()function as publicwith a return type of CBox The parametertype and name have also been entered in the appropriate fields You must click the Addbutton to registerthe parameter as being in the parameter list before clicking the Finishbutton This also updates thefunction signature shown at the bottom of the Add Member Function Wizard dialog box You could thenenter details of another parameter if there was more than one and click Add once again to add it I havealso entered a comment in the dialog box and the wizard will insert this in both Box.hand Box.cpp.When you click on Finish, the declaration for the function is added to the class definition in the Box.hfile, and a skeleton definition for the function is added to the Box.cppfile The function needs to bedeclared as constso you must add this keyword to the declaration of the operator+()function withinthe class definition, and to the definition of the function on Box.cpp You must also add the code in thebody of the function, like this:

Trang 4

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.hlooks something like this:

#pragma onceclass CBox{

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;

}

Trang 5

// 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;

m_Length = lv>wv ? lv : wv; // Ensure that

m_Width = wv<lv ? wv : lv; // length >= width

// 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

Trang 6

}// Divide one box into anotherint CBox::operator/(const CBox& aBox) const{

// Temporary for number in horizontal plane this wayint tc1 = 0;

// Temporary for number in a plane that wayint tc2 = 0;

The very short functions, particularly those that just return the value of a data member, have their nitions within the class definition so that they are inline If you take a look at Class View by clickingthe tab and then click the + beside the CBoxclass name, you’ll see that all the members of the class areshown in the lower pane

defi-This completes the CBoxclass, but you still need to define the global functions that implement operators

to compare the volume of a CBoxobject with a numerical value

Adding Global FunctionsYou need to create a cppfile that will contain the definitions for the global functions supporting opera-tions on CBoxobjects The file also needs to be part of the project Click the Solution Explorertab todisplay it (you currently will have the Class Viewtab displayed) and right-click the Source Filesfolder Select Add > New Itemfrom the context menu to display the dialog box Choose the category asCodeand 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 objectbool operator>(const double& value, const CBox& aBox){ return value > aBox.Volume(); }

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

Trang 7

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 #includedirective for Box.hbecause the functions refer to the CBoxclass Save the file.When you have completed this, you can select the Class Viewtab The Class Viewtab now includes aGlobal Functions and Variablesfolder 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 tations again When you want to use any of these functions in another cppfile, you’ll need to be surethat you declare all the functions that you use so the compiler will recognize them You can achieve this

implemen-by putting a set of declarations in a header file Switch back to the Solution Explorer pane once more andright-click the Header Filesfolder name Select Add > New Itemfrom the context menu to displaythe dialog box, but this time select category as Code,the template asHeader File(.h),and enter thename as BoxOperators After clicking the Addbutton an empty header file is added to the project, andyou can add the following code in the Editor window:

Trang 8

// BoxOperators.h - Declarations for global box operators

#pragma oncebool 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 oncedirective ensures that the contents of the file are not included more than once in abuild You just need to add an #includedirective for BoxOperators.hto any source file that makesuse of any of these functions

You’re now ready to start applying these functions, along with the CBoxclass, to a specific problem inthe world of boxes

Using Our CBox Class

Suppose that you are packaging candies The candies are on the big side, real jaw breakers, occupying anenvelope 1.5 inches long by 1 inch wide by 1 inch high You have access to a standard candy box that is4.5 inches by 7 inches by 2 inches, and you want to know how many candies will fit in the box so thatyou 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 spaceyou’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 candybox would be suitable You know that you can get a good price on boxes with a length from 3 inches to 7inches, a width from 3 inches to 5 inches and a height from 1 inch to 2.5 inches, where each dimensioncan 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 candybox should not have empty space, because the complaints from customers who think they are beingcheated goes up Further, ideally you want to pack the standard carton completely so the candies don’trattle around You don’t want to be too stringent about this otherwise packing could become difficult, solet’s say you have no wasted space if the free space in the packed carton is less than the volume of a sin-gle candy box

With the CBoxclass, the problem becomes almost trivial; the solution is represented by the followingmain()function Add a new C++ source file, Ex8_08.cpp, to the project through the context menu youget when you right-click Source Filesin the Solution Explorerpane, as you’ve done before Youcan then type in the code shown here:

Trang 9

int numCandies = candyBox/candy;

// Calculate candy boxes per cartonint numCboxes = carton/candyBox;

// Calculate wasted carton spacedouble 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 boxesfor(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 cycleCBox 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;

}

Trang 10

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

Figure 8-13

The file Ex8_08.cppcontains the main()function and a #includedirective for the file BoxOperators.hthat contains the prototypes for the functions in BoxOperators.cpp(which aren’t class members) It alsohas an #includedirective for the definition of the class CBoxin Box.h A C++ console program is usuallydivided into a number of files that will each fall into one of three basic categories:

1. hfiles containing library #includecommands, global constants and variables, class tions and function prototypes — in other words, everything except executable code They alsocontain inline function definitions Where a program has several class definitions, they are oftenplaced in separate hfiles

defini-2. cppfiles containing the executable code for the program, plus #includecommands for all thedefinitions required by the executable code

3. Another .cppfile 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 oriented actions on CBoxobjects

problem-The solution to the question of the use of standard boxes is in the declaration statements, which alsocompute the answers we require as initializing values You then output these values with some explana-tory comments

Trang 11

The second part of the problem is solved using the three nested forloops iterating over the possibleranges of m_Length, m_Widthand m_Heightso that you evaluate all possible combinations You couldoutput them all as well, but because this would involve 200 combinations, of which you might only beinterested in a few, you have an ifstatement that identifies the options that you’re actually interested in.The ifexpression is only trueif 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 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 alength 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 theCBoxclass constructor ensures that the length is not less than the width, these two are identical Youcould 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 mon practice with C++ applications generally, but with Windows programming it is essential The sheervolume of code involved in even the simplest program necessitates dividing it into workable chunks

com-As discussed in the previous section, there are basically two kinds of source code file in a C++ program,.hfiles and cppfiles This is illustrated in Figure 8-14

There’s the executable code that corresponds to the definitions of the functions that make up the gram Also, there are definitions of various kinds that are necessary for the executable code to compilecorrectly These are global constants and variables, data types that include classes, structures, andunions, 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

Trang 12

pro-Figure 8-14

From time to time, you might want to use code from existing files in a new project In this case you onlyhave to add the cppfiles to the project, which you can do by using the Project > Add ExistingItemmenu option, or by right-clicking either Source Filesor Header Filesin the SolutionExplorertab and selecting Add > Existing Itemfrom the context menu to add the file to your pro-ject You don’t need to add hfiles to your project, although you can if you want them to be shown inthe Solution Explorerpane immediately The code from hfiles is added at the beginning of the cppfiles that require them as a result of the #includedirectives that you specify You need #includedirec-tives for header files containing standard library functions and other standard definitions, as well as foryour own header files Visual C++ 2005 automatically keeps track of all these files, and enables you toview them in the Solution Explorertab As you saw in the last example, you can also view the classdefinitions and global constants and variables in the Class Viewtab

Function Definitions

Function DefinitionsFunction DefinitionsDefinition of main()

Source fileswith the extension cpp

Class Definition

Class DefinitionGlobal ConstantsGlobal Constants

Header fileswith the extension h

Trang 13

In a Windows program, there are other kinds of definitions for the specification of things such as menusand toolbar buttons These are stored in files with extensions like rcand ico Just like hfiles, these

do not need to be explicitly added to a project because they are created and tracked automatically byVisual C++ 2005 when you need them

Naming Program Files

As I have already said, for classes of any complexity, it’s usual to store the class definition in a hfilewith a filename based on the class name, and to store the implementation of the function members of theclass that are defined outside the class definition in a cppfile with the same name On this basis, thedefinition of our CBoxclass appeared in a file with the name Box.h Similarly, the class implementationwas stored in the file Box.cpp We didn’t follow this convention in the earlier examples in the chapterbecause the examples were very short, and it was easier to reference the examples with names derivedfrom the chapter number and the sequence number of the example within the chapter With programs ofany size though it becomes essential to structure the code in this way, so it would be a good idea to getinto the habit of creating h and cpp files to hold your program code from now on

Segmenting a C++ program into hand cppfiles is a very convenient approach, as it makes it easy foryou to find the definition or implementation of any class, particularly if you’re working in a develop-ment environment that doesn’t have all the tools that Visual C++ provides As long as you know theclass name, you can go directly to the file you want This isn’t a rigid rule, however It’s sometimes use-ful to group the definitions of a set of closely related classes together in a single file and assemble theirimplementations similarly; however you choose to structure your files, the Class View still displays allthe individual classes, as well as all the members of each class, as you can see in Figure 8-15

Figure 8-15

Trang 14

I adjusted the size of the Class View pane so all the elements in the project are visible Here, you can seethe details of the classes and globals for the last example As mentioned before, double-clicking any ofthe entries in the tree takes you directly to the relevant source code.

C++/CLI Programming

Although you can define a destructor in a reference class in the same way as you do for native C++classes, most of the time it is not necessary; however, I’ll return to the topic of destructors for referenceclasses in the next chapter You can also call delete for a handle to a reference class, but again, this is notnormally necessary as the garbage collector will delete unwanted objects automatically

C++/CLI classes support overloading of operators but there are some differences that you need toexplore First of all, consider some basic differences between operator overloading C++/CLI classes and

in native C++ classes A couple of differences you have already heard about You’ll probably recall thatyou must not overload the assignment operator in your value classes because the process for the assign-ment of one value class object to another of the same type is already defined to be member-by-membercopying and you cannot change this I also mentioned that unlike native classes, a ref class does not have

a default assignment operator(if you want the assignment operator to work with your ref class objectsthen you must implement the appropriate function Another difference from native C++ classes is thatfunctions that implement operator overloading in C++/CLI classes can be static members of a class aswell as instance members This means that you have the option of implementing binary operators inC++/CLI classes with static member functions with two parameters in addition to the possibilities youhave seen in the context of native C++ for operator functions as instance functions with one parameter

or non-member functions with two parameters Similarly, in C++/CLI you have the additional ity to implement a prefix unary operator as a static member function with no parameters Finally, although

possibil-in native C++ you can overload the newoperator, you cannot overload the gcnewoperator in a C++/CLIclass

Let’s look into some of the specifics, starting with value classes

Overloading Operators in Value Classes

Define a class to represent a length in feet and inches and use that as a base for demonstrating how ator overloading can be implemented for a value class Addition seems like a good place to start, sohere’s the Lengthvalue class, complete with the addition operator function:

oper-value class Length{

private:

int feet; // Feet componentint inches; // Inches componentpublic:

static initonly int inchesPerFoot = 12;

// ConstructorLength(int ft, int ins) : feet(ft), inches(ins){ }// A length as a string

Trang 15

virtual String^ ToString() override

{ return feet+L” feet “ + inches + L” inches”; }

// Addition operator

Length operator+(Length len)

{

int inchTotal = inches+len.inches+inchesPerFoot*(feet+len.feet);

return Length(inchTotal/inchesPerFoot, inchTotal%inchesPerFoot);

}

};

The constant, inchesPerFootis staticso it is directly available to static and non-static function bers of the class Declaring inchesPerFootas initonlymeans that it cannot be modified so it can be apublic member of the class There’s a ToString()function override defined for the class so you canwrite Lengthobjects to the command line using the Console::WriteLine()function The operator+()function implementation is very simple The function returns a new Lengthobject produced by combin-ing the feetand inchescomponent for the current object and the parameter, len The calculation isdone by combining the two lengths in inches and then computing the arguments to the Lengthclassconstructor for the new object from the value for the combined lengths in inches

mem-The following code fragment would exercise the new operator function for addition

Length len1 = Length(6, 9);

Length len2 = Length(7, 8);

Console::WriteLine(L”{0} plus {1} is {2}”, len1, len2, len1+len2);

The last argument to the WriteLine()function is the sum of two Lengthobjects so this invokes theoperator+()function The result is a new Lengthobject for which the compiler arranges to call theToString()function so the last statement is really the following:

Console::WriteLine(L”{0} plus {1} is {2}”, len1, len2,

len1.operator+(len2).ToString()); The execution of the code fragment results in the following output:

6 feet 9 inches plus 7 feet 8 inches is 14 feet 5 inches

Of course, you could define the operator+()function as a static member of the Lengthclass, like this:static Length operator+(Length len1, Length len2)

Trang 16

Because we are not working with areas multiplication for Lengthobjects really only makes sense plying a length by a numerical value You can implement this as a static member of the class, but let’sdefine the function outside the class The class looks like this:

multi-value class Length{

virtual String^ ToString() override{ return feet+L” feet “ + inches + L” inches”; }// Addition operator

Length operator+(Length len){

int inchTotal = inches+len.inches+inchesPerFoot*(feet+len.feet);

return Length(inchTotal/inchesPerFoot, inchTotal%inchesPerFoot);

}static Length operator*(double x, Length len); // Pre-multiply by a double valuestatic Length operator*(Length len, double x); // Post-multiply by a double value};

The new function declarations in the class provide for overloaded * operator functions to pre- and multiply a Lengthobject by a value of type double The definition of the operator*()function out-side the class for pre-multiplication is:

post-Length post-Length::operator *(double x, post-Length len){

int ins = safe_cast<int>(x*len.inches +x*len.feet*inchesPerFoot);

return Length(ins/12, ins %12);

}The post-multiplication version can now be implemented in terms of this:

Length Length::operator *(Length len, double x){ return operator*(x, len); }

This just call the pre-multiply version with the arguments reversed You could exercise these functionswith the following fragment:

double factor = 2.5;

Console::WriteLine(L”{0} times {1} is {2}”, factor, len2, factor*len2);

Console::WriteLine(L”{1} times {0} is {2}”, factor, len2, len2*factor);

Trang 17

Both lines of output from this code fragment should reflect the same result from multiplication(19 feet 2inches The argument expression factor*len2is equivalent to:

We could expand a little further on overloaded operators in the Lengthclass with a working example

Try It Out A Value Class with Overloaded Operators

This example implements operator overloading for addition, multiplication, and division for the Lengthclass:

// Ex8_09.cpp : main project file

// Overloading operators in the value class, Length

#include “stdafx.h”

using namespace System;

value class Length

virtual String^ ToString() override

{ return feet+L” feet “ + inches + L” inches”; }

// Addition operator

Length operator+(Length len)

{

int inchTotal = inches+len.inches+inchesPerFoot*(feet+len.feet);

return Length(inchTotal/inchesPerFoot, inchTotal%inchesPerFoot);

}

// Division operator

static Length operator/(Length len, double x)

{

int ins = safe_cast<int>((len.feet*inchesPerFoot + len.inches)/x);

return Length(ins/inchesPerFoot, ins%inchesPerFoot);

Trang 18

}static Length operator*(double x, Length len); // Pre-multiply by a double valuestatic Length operator*(Length len, double x); // Post-multiply by a double value};

Length Length::operator *(double x, Length len){

int ins = safe_cast<int>(x*len.inches +x*len.feet*inchesPerFoot);

return Length(ins/inchesPerFoot, ins%inchesPerFoot);

}Length Length::operator *(Length len, double x){ return operator*(x, len); }

int main(array<System::String ^> ^args){

Length len1 = Length(6, 9);

Length len2 = Length(7, 8);

double factor = 2.5;

Console::WriteLine(L”{0} plus {1} is {2}”, len1, len2, len1+len2);

Console::WriteLine(L”{0} times {1} is {2}”, factor, len2, factor*len2);

Console::WriteLine(L”{1} times {0} is {2}”, factor, len2, len2*factor);

Console::WriteLine(L”The sum of {0} and {1} divided by {2} is {3}”,

len1, len2, factor, (len1+len2)/factor);return 0;

}The output from the example is:

6 feet 9 inches plus 7 feet 8 inches is 14 feet 5 inches2.5 times 7 feet 8 inches is 19 feet 2 inches

7 feet 8 inches times 2.5 is 19 feet 2 inchesThe sum of 6 feet 9 inches and 7 feet 8 inches divided by 2.5 is 5 feet 9 inchesPress any key to continue

How It WorksThe new operator overloading function in the Lengthclass is for division and it allows division of aLengthvalue by a value of type double Dividing a doublevalue by a Lengthobject does not have anobvious meaning so there’s no need to implement this version The operator/()function is imple-mented as another static member of the class and the definition appears within the body of the class def-inition to contrast how that looks compared with the operator*()functions You would normallydefine all these functions inside the class definition

Of course, you could define the operator/()function as a non-static class member like this:

Length operator/(double x){

int ins = safe_cast<int>((feet*inchesPerFoot + inches)/x);

return Length(ins/inchesPerFoot, ins%inchesPerFoot);

}

Trang 19

It now has one argument that is the right operand for the /operator The left operand is the currentobject referenced by the thispointer (implicitly in this case).

The operators are exercised in the four output statements Only the last one is new to you and this bines the use of the overloaded +operator for Lengthobjects with the overloaded /operator The lastargument to the Console::WriteLine()function in the fourth output statement is (len1+len2)/factorwhich is equivalent to the expression:

com-Length::operator/(len1.operator+(len2), factor) ToString()

The first argument to the static operator/()function is the Lengthobject that is returned by theoperator+()function, and the second argument is the factorvariable, which is the divisor TheToString()function for the Lengthobject returned by operator/()is called to produce the argumentstring that is passed to the Console::WriteLine()function

It is possible that you might want the capability to divide one Lengthobject by another and have avalue of type int as the result This would allow you to figure out how many 17 inch lengths you can cutfrom a piece of timber 12 feet six inches long for instance You can implement this quite easily like this:static int operator/(Length len1, Length len2)

{

return

(len1.feet*inchesPerFoot + len1.inches)/( len2.feet*inchesPerFoot + len2.inches);}

This just returns the result of dividing the first length in inches by the second in inches

To complete the set you could add a function to overload the %operator to tell you how much is leftover This could be implemented as:

static Length operator%(Length len1, Length len2)

{

int ins = (len1.feet*inchesPerFoot + len1.inches)%

(len2.feet*inchesPerFoot + len2.inches);return Length(ins/inchesPerFoot, ins%inchesPerFoot);

}

You compute the residue in inches after dividing len1by len2and return it as a new Lengthobject With all these operators you really can use your Lengthobjects in arithmetic expressions You can writestatements such as:

Length len1 = Length(2,6); // 2 feet 6 inches

Length len2 = Length(3,5); // 3 feet 5 inches

Length len3 = Length(14,6); // 14 feet 6 inches

Length total = 12*(len1 + len2 + len3) + (len3/Length(1,7))*len2;

The value of totalwill be 275 feet 9 inches The last statement makes use of the assignment operatorthat comes with every value class as well as the operator*(), operator+(), and operator/()func-tions in the Lengthclass This operator overloading is not only powerful stuff, it really is easy isn’t it?

Trang 20

Overloading the Increment and Decrement Operators

Overloading the increment and decrement is simpler in C++/CLI that in native C++ As long as youimplement the operator function as a static class member, the same function will serve as both the prefixand postfix operator functions Here’s how you could implement the increment operator for the Lengthclass:

class Length{

len.feet += len.inches/len.inchesPerFoot;

len.inches %= len.inchesPerFoot;

return len;

}}This implementation of the operator++()function increments a length by 1 inch The following codeexercises the function:

Length len = Length(1, 11); // 1 foot 11 inchesConsole::WriteLine(len++);

Console::WriteLine(++len);

Executing this fragment produces the output:

1 feet 11 inches

2 feet 1 inchesThus the prefix and postfix increment operations are working as they should using a single operatorfunction in the Lengthclass This occurs because the compiler is able to determine whether to use thevalue of the operand in a surrounding expression before or after the operand has been incremented andcompile the code accordingly

Overloading Operators in Reference Classes

Overloading operators in a reference class is essentially the same as overloading operators in a valueclass, the primary difference being that parameters and return values are typically handles Let’s seehow the Lengthclass looks implemented as a reference class; then you can compare the two versions

Try It Out Overloaded Operators in a Reference Class

This example defines Lengthas a reference class with the same set of overloaded operators as the valueclass version:

Trang 21

// Ex8_10.cpp : main project file.

// Defining and using overloaded operator

#include “stdafx.h”

using namespace System;

ref class Length

virtual String^ ToString() override

{ return feet+L” feet “ + inches + L” inches”; }

// Overloaded addition operator

Length^ operator+(Length^ len)

{

int inchTotal = inches+len->inches+inchesPerFoot*(feet+len->feet);

return gcnew Length(inchTotal/inchesPerFoot, inchTotal%inchesPerFoot);

}

// Overloaded divide operator - right operand type double

static Length^ operator/(Length^ len, double x)

{

int ins = safe_cast<int>((len->feet*inchesPerFoot + len->inches)/x);

return gcnew Length(ins/inchesPerFoot, ins%inchesPerFoot);

}

// Overloaded divide operator - both operands type Length

static int operator/(Length^ len1, Length^ len2)

{

return (len1->feet*inchesPerFoot + len1->inches)/

(len2->feet*inchesPerFoot + len2->inches);}

// Overloaded remainder operator

static Length^ operator%(Length^ len1, Length^ len2)

{

int ins = (len1->feet*inchesPerFoot + len1->inches)%

(len2->feet*inchesPerFoot + len2->inches);return gcnew Length(ins/inchesPerFoot, ins%inchesPerFoot);

}

static Length^ operator*(double x, Length^ len); // Multiply - R operand doublestatic Length^ operator*(Length^ len, double x); // Multiply - L operand double // Pre- and postfix increment operator

Trang 22

static Length^ operator++(Length^ len){

// Multiply operator implementation - right operand doubleLength^ Length::operator*(double x, Length^ len)

{int ins = safe_cast<int>(x*len->inches +x*len->feet*inchesPerFoot);

return gcnew Length(ins/inchesPerFoot, ins%inchesPerFoot);

}// Multiply operator implementation - left operand doubleLength^ Length::operator*(Length^ len, double x)

{ return operator*(x, len); }int main(array<System::String ^> ^args){

Length^ len1 = gcnew Length(2,6); // 2 feet 6 inchesLength^ len2 = gcnew Length(3,5); // 3 feet 5 inchesLength^ len3 = gcnew Length(14,6); // 14 feet 6 inches// Use +, * and / operators

Length^ total = 12*(len1+len2+len3) + (len3/gcnew Length(1,7))*len2;

Console::WriteLine(total);

// Use remainder operatorConsole::WriteLine(

L”{0} can be cut into {1} pieces {2} long with {3} left over.”,

len3, len3/len1, len1, len3%len1);Length^ len4 = gcnew Length(1, 11); // 1 foot 11 inches

// Use pre- and postfix increment operatorConsole::WriteLine(len4++); // Use postfix increment operatorConsole::WriteLine(++len4); // Use postfix increment operatorreturn 0;

}This example produces the following output:

Trang 23

How It Works

The main differences are in the parameter and return types for the overloaded operator functions, theuse of the ->operator as a consequence of that, and objects of type Lengthare now created on the CLRheap using the gcnewkeyword Apart from these changes the code is basically the same and the opera-tor functions work just as effectively as in the previous example

Summar y

In this chapter, you learned the basics of how you can define classes and how you create and use classobjects You have also learned about how you can overload operators in a class to allow the operators to

be applied to class objects

The key points to keep in mind from this chapter are:

Objects are created by functions called constructors The primary role of a constructor is to set

values for the data members (fields) for a class object

C++/CLI classes can also have a static contructor that initializes the static fields in a class.

Objects are destroyed using functions called destructors It is essential to define a destructor in

native C++ classes to destroy objects which contain members that are allocated on the heapbecause the default constructor will not do this

❑ The compiler will supply a default copy constructor for a native C++ class if you do not defineone The default copy constructor will not deal correctly with objects of classes that have datamembers allocated on the free store

❑ When you define your own copy constructor in a native C++ class, you must use a referenceparameter

❑ You must not define a copy constructor in a value classes; copies of value class objects arealways created by copying fields

❑ No default copy constructor is supplied for a reference class although you can define your ownwhen this is necessary

❑ If you do not define an assignment operator for your native C++ class, the compiler will supply

a default version As with the copy constructor, the default assignment operator will not workcorrectly with classes that have data members allocated on the free store

❑ You must not define the assignment operator in a value class Assignment of value class objects

is always done by copying fields

❑ A default assignment operator is not provided is reference classes but you can define your ownassignment operator function when necessary

❑ It is essential that you provide a destructor, a copy constructor and an assignment operator fornative C++ classes that have members allocated by new

❑ A union is a mechanism that allows two or more variables to occupy the same location in memory

Trang 24

C++/CLI classes can contain literal fields that define constants within a class The can also tain initonly fields that are cannot be modified once they have been initialized.

con-❑ Most basic operators can be overloaded to provide actions specific to objects of a class Youshould only implement operator functions for your classes that are consistent with the normalinterpretation of the basic operators

❑ A class template is a pattern that you can use to create classes with the same structure but port different data types

sup-❑ You can define a class template that has multiple parameters, including parameters that canassume constant values rather than types

❑ You should put definitions for your program in hfiles, and executable code — function definitions — in cppfiles You can then incorporate hfiles into your cppfiles by using

inte-Provide one or more constructors for such a class Overload the +operator so that these integerscan be used in arithmetic expressions Do you want the +operator to be a global or a memberfunction? Do you need an assignment operator? Provide a Print()member function so thatthey can be printed out, using a leading ‘E’ to denote that the ‘estimation’ flag is set Write aprogram to test the operation of your class, checking especially that the operation of the estima-tion flag is correct

2. Implement a simple string class in native C++ that holds a char*and an integer length as privatedata members Provide a constructor which takes an argument of type const char*,and implement the copy constructor, assignment operator and destructor functions Verify thatyour class works You will find it easiest to use the string functions from the <cstring>headerfile

3. What other constructors might you want to supply for your string class? Make a list, and codethem up

4. (Advanced) Does your class correctly deal with cases such as this?

Trang 25

5. (Advanced) Overload the +and +=operators of your class for concatenating strings.

6. Modify the stack example from Exercise 7 in the previous chapter so that the size of the stack isspecified in the constructor and dynamically allocated What else do you need to add? Test theoperation of your new class

7. Define a Box ref class with the same functionality as the CBoxclass in Ex8_08.cppand ment the example as a program for the CLR

Trang 26

Class Inheritance and

V ir tual Functions

In this chapter, you’re going to look into a topic that lies at the heart of object-oriented

program-ming class inheritance Simply put, inheritance is the means by which you can define a new class

in terms of one you already have This is fundamental to programming in C++ so it’s importantthat you understand how inheritance works

In this chapter, you will learn about:

❑ How inheritance fits into the idea of object-oriented programming

❑ Defining a new class in terms of an existing one

❑ The use of the protectedkeyword to define a new access specification for class members

❑ How a class can be a friend to another class

❑ Virtual functions and how you can use them

❑ Pure virtual functions

❑ Abstract classes

❑ Virtual destructors and when to use them

Basic Ideas of OOP

As you have seen, a class is a data type that you define to suit your own application requirements.Classes in object-oriented programming also define the objects to which your program relates Youprogram the solution to a problem in terms of the objects that are specific to the problem, usingoperations that work directly with those objects You can define a class to represent somethingabstract, such as a complex number, which is a mathematical concept, or a truck, which is decid-edly physical (especially if you run into one on the highway) So, as well as being a data type, aclass can also be a definition of a set of real-world objects of a particular kind, at least to the degreenecessary to solve a given problem

Trang 27

You can think of a class as defining the characteristics of a particular group of things that are specified by

a common set of parameters and share a common set of operations that may be performed on them Theoperations that you can apply to objects of a given class type are defined by the class interface, whichcorresponds to the functions contained in the publicsection of the class definition The CBoxclass thatyou used in the previous chapter is a good example — it defined a box in terms of its dimensions plus aset of public functions that you could apply to CBoxobjects to solve a problem

Of course, there are many different kinds of boxes in the real world: There are cartons, coffins, candyboxes, and cereal boxes, to name but a few, and you will certainly be able to come up with many others.You can differentiate boxes by the kinds of things they hold, the materials with which they are made,and in a multitude of other ways, but even though there are many different kinds of box, they share

some common characteristics — the essence of boxiness perhaps Therefore you can still visualize all

kinds of boxes as actually being related to one another, even though they have many differentiating features You could define a particular kind of box as having the generic characteristics of all boxes —perhaps just a length, a width and a height You could then add some additional characteristics to thebasic box type to differentiate a particular kind of box from the rest You may also find that there are new things you can do with your specific kind of box that you can’t do with other boxes

It’s also possible that some objects may be the result of combining a particular kind of box with someother type of object: a box of candy or a crate of beer, for example To accommodate this you coulddefine one kind of box as a generic box with basic boxiness characteristics and then specify another sort

of box as a further specialization of that Figure 9-1 illustrates an example of the kinds of relationshipsyou might define between different sorts of boxes

Figure 9-1

class CBoxm_Lengthm_Widthm_HeightMore General

class CCandyBoxm_Contents

class CCratem_nBottles

class CBeerCratem_Beer

class CCartonm_MaxWeight

More Specialized

Trang 28

The boxes become more specialized as you move down the diagram and the arrows run from a givenbox type to the one on which it is based Figure 9-1 defines three different kinds of box based on thegeneric type, CBox It also defines beer crates as a further refinement of crates designed to hold bottles.Thus a good way to approximate the real world relatively well using classes in C++ is through the abil-ity to define classes that are interrelated A candy box can be considered to be a box with all the charac-teristics of a basic box, plus a few characteristics of its own This precisely illustrates the relationshipbetween classes in C++ when one class is defined based on another A more specialized class has all thecharacteristics of the class on which it is based, plus a few characteristics of its own that identify whatmakes it special Let’s look at how this works in practice.

Inheritance in Classes

When you define one class based on another, the former is referred to as a derived class A derived class

automatically contains all the data members of the class that you used to define it and, with some

restric-tions, the function members as well The class is said to inherit the data members and function members

of the class on which it is based

The only members of a base class that are not inherited by a derived class are the destructor, the structors, and any member functions overloading the assignment operator All other function members,together with all the data members of a base class, are inherited by a derived class Of course, the reasonfor certain base members not being inherited is that a derived class always has its own constructors anddestructor If the base class has an assignment operator, the derived class provides its own version.When I say these functions are not inherited, I mean that they don’t exist as members of a derived classobject However, they still exist for the base class part of an object, as you will see

con-What Is a Base Class?

A base class is any class that you use as a basis for defining another class For example, if you define a

class Bdirectly in terms of a class A, Ais said to be a direct base class of B In Figure 9-1 the CCrateclass

is a direct base class of CBeerCrate When a class such as CBeerCrateis defined in terms of anotherclass CCrate, CBeerCrateis said to be derived from CCrate Because CCrateis itself defined in terms of the class CBox, CBoxis said to be an indirect base class of CBeerCrate You’ll see how this isexpressed in the class definition in a moment Figure 9-2 illustrates the way in which base class membersare inherited in a derived class

Trang 29

Figure 9-2

Just because member functions are inherited doesn’t mean that you won’t want to replace them in thederived class with new versions, and, of course, you can do that when necessary

Deriving Classes from a Base Class

Let’s go back to the original CBoxclass with publicdata members that you saw at the beginning of theprevious chapter:

// Header file Box.h in project Ex9_01

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

m_Length(lv), m_Width(wv), m_Height(hv){}

};

Create a new empty WIN32 console project with the name Ex9_01 and save this code in a new headerfile in the project with the name Box.h The #pragma oncedirective ensures the definition of CBoxappear only once in a build There’s a constructor in the class so that you can initialize objects when youdeclare them Suppose you now need another class of objects, CCandyBox, that are the same as CBoxobjects but also have another data member — a pointer to a text string — that identifies the contents ofthe box

You can define CCandyBoxas a derived class with the CBoxclass as the base class, as follows:

// Header file CandyBox.h in project Ex9_01

#pragma once

#include “Box.h”

Derived ClassData MembersFunction Members

Other Overloaded OperatorsOwn Data MembersOwn Function MembersOwn ConstructorsOwn Destructors

Base ClassData MembersFunction MembersConstructorsDestructorOverloaded = operatorOther Overloaded Operators

/ No

InheritedMembers

/ No/ No

Trang 30

class CCandyBox: CBox{

};

Add this header file to the project Ex9_01 You need the #includedirective for the Box.hheader filebecause you refer to the CBoxclass in the code If you were to leave this directive out, CBoxwould beunknown to the compiler so the code would not compile The base class name, CBox, appears after thename of the derived class CCandyBoxand is separated from it by a colon In all other respects, it lookslike a normal class definition You have added the new member, m_Contents, and, because it is apointer to a string, you need a constructor to initialize it and a destructor to release the memory for thestring You have also put a default value for the string describing the contents of a CCandyBoxobject inthe constructor Objects of the CCandyBoxclass type contain all the members of the base class, CBox,plus the additional data member, m_Contents

Note the use of the strcpy_s()function that you first saw in Chapter 6 Here there are three arguments —the destination for the copy operation, the length of the destination buffer, and the source If both arrayswere static — that is, not allocated on the heap — you could omit the second argument and just supply thedestination and source pointers This is possible because the strcpy_s()function is also available as atemplate function that can infer the length of the destination string automatically You can therefore call thefunction just with the destination and source strings as arguments when you are working with staticstrings

Try It Out Using a Derived Class

Now you’ll see how the derived class works in an example Add the following code to the Ex9_01ject as the source file Ex9_01.cpp:

pro-// Ex9_01.cpp// Using a derived class

#include <iostream> // For stream I/O

#include <cstring> // For strlen() and strcpy()

#include “CandyBox.h” // For CBox and CCandyBoxusing std::cout;

using std::endl;

int main(){

CBox myBox(4.0, 3.0, 2.0); // Create CBox objectCCandyBox myCandyBox;

Trang 31

CCandyBox myMintBox(“Wafer Thin Mints”); // Create CCandyBox object

cout << endl

<< “myBox occupies “ << sizeof myBox // Show how much memory

<< “ bytes” << endl // the objects require

<< “myCandyBox occupies “ << sizeof myCandyBox

con-After declaring a CBoxobject and two CCandyBoxobjects, you output the number of bytes that eachobject occupies Let’s look at the output:

myBox occupies 24 bytes

myCandyBox occupies 32 bytes

myMintBox occupies 32 bytes

myBox length is 4

The first line is what you would expect from the discussion in the previous chapter ACBoxobject hasthree data members of type double, each of which is 8 bytes, making 24 bytes in all Both the CCandyBoxobjects are the same size — 32 bytes The length of the string doesn’t affect the size of an object, as thememory to hold the string is allocated in the free store The 32 bytes are made up of 24 bytes for the threedoublemembers inherited from the base class CBox, plus 4 bytes for the pointer member m_Contents,which makes 28 bytes So where did the other four bytes come from? This is due to the compiler aligningmembers at addresses that are multiples of eight bytes You should be able to demonstrate this byadding an extra member of type int, say, to the class CCandyBox You will find that the size of a classobject is still 32 bytes

You also output the value of the m_Lengthmember of the CBoxobject myBox Even though you have nodifficulty accessing this member of the CBoxobject, if you uncomment the following statement in thefunction main(),

// myCandyBox.m_Length = 10.0; // uncomment this for an error

Trang 32

the program no longer compiles The compiler generates the following message:

error C2247: ‘CBox::m_Length’ not accessible because ‘CCandyBox’ uses ‘private’ toinherit from ‘CBox’

It says quite clearly that the m_Lengthmember from the base class is not accessible because m_Lengthhas become privatein the derived class This is because there is a default access specifier of privatefor a base class when you define a derived class — it’s as if the first line of the derived class definitionhad been

class CCandyBox: private CBoxThere always has to be an access specification for a base class that determines the status of the inheritedmembers in the derived class If you omit the access specification for a base class, the compiler assumesthat it’s private If you change the definition of the CCandyBoxclass in CandyBox.hto the following,class CCandyBox: public CBox

{public:

};

the m_Lengthmember is inherited in the derived class as publicand is accessible in the functionmain() With the access specifier publicfor the base class, all the inherited members originally speci-fied as publicin the base class have the same access level in the derived class

Access Control Under Inheritance

The whole question of the access of inherited members in a derived class needs to be looked at moreclosely Consider the status of the privatemembers of a base class in a derived class

There was a good reason to choose the version of the class CBoxwith publicdata members in the ous example, rather than the later, more secure version with privatedata members The reason wasthat although privatedata members of a base class are also members of a derived class, they remainprivateto the base class in the derived class so member functions added to the derived class cannotaccess them They are only accessible in the derived class through function members of the base classthat are not in the privatesection of the base class You can demonstrate this very easily by changingall the CBoxclass data members to privateand putting a Volume()function in the derived classCCandyBox, so that the class definition is as follows:

Trang 33

previ-// Version of the classes that will not compile

class CBox

{

public:

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

m_Length(lv), m_Width(wv), m_Height(hv){}

CCandyBox(char* str = “Candy”) // Constructor{

m_Contents = new char[ strlen(str) + 1 ];

strcpy_s(m_Contents, strlen(str) + 1, str);

}

~CCandyBox() // Destructor{ delete[] m_Contents; }

};

A program using these classes does not compile The function Volume()in the class CCandyBoxattempts to access the privatemembers of the base class, which is not legal

Try It Out Accessing Private Members of the Base Class

It is, however, legal to use the Volume()function in the base class, so if you move the definition of thefunction Volume()to the publicsection of the base class, CBox, not only will the program compile butyou can use the function to obtain the volume of a CCandyBoxobject Create a new WIN32 project,Ex9_02, with the Box.hcontents as the following:

Trang 34

m_Length(lv), m_Width(wv), m_Height(hv){}

//Function to calculate the volume of a CBox objectdouble Volume() const

{ return m_Length*m_Width*m_Height; }private:

double m_Length;

double m_Width;

double m_Height;

};

The CandyBox.hheader in the project contains:

// Header file CandyBox.h in project Ex9_02

};

The Ex9_02.cppfile in the project contains:

// Ex9_02.cpp// Using a function inherited from a base class

#include <iostream> // For stream I/O

#include <cstring> // For strlen() and strcpy()

#include “CandyBox.h” // For CBox and CCandyBoxusing std::cout;

using std::endl;

int main(){

CBox myBox(4.0,3.0,2.0); // Create CBox objectCCandyBox myCandyBox;

CCandyBox myMintBox(“Wafer Thin Mints”); // Create CCandyBox objectcout << endl

<< “myBox occupies “ << sizeof myBox // Show how much memory

<< “ bytes” << endl // the objects require

<< “myCandyBox occupies “ << sizeof myCandyBox

Trang 35

return 0;

}

This example produces the following output:

myBox occupies 24 bytes

myCandyBox occupies 32 bytes

myMintBox occupies 32 bytes

myMintBox volume is 1

How It Works

The interesting additional output is the last line This shows the value produced by the function

Volume(), which is now in the publicsection of the base class Within the derived class, it operates onthe members of the derived class that are inherited from the base It is a full member of the derived class,

so it can be used freely with objects of the derived class

The value for the volume of the derived class object is 1 because, in creating the CCandyBoxobject, theCBox()default constructor was called first to create the base class part of the object, and this sets defaultCBoxdimensions to 1

Constructor Operation in a Derived Class

Although I said the base class constructors are not inherited in a derived class, they still exist in the baseclass and are used for creating the base part of a derived class object This is because creating the baseclass part of a derived class object is really the business of a base class constructor, not the derived classconstructor After all, you have seen that private members of a base class are inaccessible in a derivedclass object, even though they are inherited, so responsibility for these has to lie with the base class con-structors

The default base class constructor was called automatically in the last example to create the base part ofthe derived class object, but this doesn’t have to be the case You can arrange to call a particular baseclass constructor from the derived class constructor This enables you to initialize the base class datamembers with a constructor other than the default, or indeed to choose to call a particular class construc-tor, depending on the data supplied to the derived class constructor

Try It Out Calling Constructors

You can see this in action through a modified version of the previous example To make the class usable,you really need to provide a constructor for the derived class that allows you to specify the dimensions

of the object You can add an additional constructor in the derived class to do this, and call the base classconstructor explicitly to set the values of the data members that are inherited from the base class

Trang 36

In the Ex9_03project, Box.hcontains:

:CBox(lv, wv, hv){

cout << endl <<”CCandyBox constructor2 called”;

m_Contents = new char[ strlen(str) + 1 ];

strcpy_s(m_Contents, strlen(str) + 1, str);

}// Constructor to set contents// calls default CBox constructor automaticallyCCandyBox(char* str = “Candy”)

{

Trang 37

cout << endl << “CCandyBox constructor1 called”;

m_Contents = new char[ strlen(str) + 1 ];

strcpy_s(m_Contents, strlen(str) + 1, str);

}

~CCandyBox() // Destructor{ delete[] m_Contents; }

};

The #includedirective for the <iostream>header and the two using declarations are not strictly essary here because Box.hcontains the same code, but it does no harm to put them in On the contrary,putting these statements in here also means that if you were to remove this code from Box.hbecause itwas no longer required there, CandyBox.hstill compiles

nec-The contents of Ex9_03.cppis:

// Ex9_03.cpp

// Calling a base constructor from a derived class constructor

#include <iostream> // For stream I/O

#include <cstring> // For strlen() and strcpy()

#include “CandyBox.h” // For CBox and CCandyBox

<< “myBox occupies “ << sizeof myBox // Show how much memory

<< “ bytes” << endl // the objects require

<< “myCandyBox occupies “ << sizeof myCandyBox

<< “ bytes” << endl

<< “myMintBox occupies “ << sizeof myMintBox

<< “ bytes”;

cout << endl

<< “myMintBox volume is “ // Get volume of a

<< myMintBox.Volume(); // CCandyBox objectcout << endl;

return 0;

}

How It Works

As well as adding the additional constructor in the derived class, you have added an output statement

in each constructor so you know when either gets called The explicit call of the constructor for the CBoxclass appears after a colon in the function header of the derived class constructor You have perhapsnoticed that the notation is exactly the same as what you have been using for initializing members in aconstructor anyway:

Trang 38

// Calling the base class constructorCCandyBox(double lv, double wv, double hv, char* str= “Candy”):

CBox(lv, wv, hv){

}This is perfectly consistent with what you are doing here because you are essentially initializing a CBoxsub-object of the derived class object In the first case, you are explicitly calling the default constructorfor the doublemembers m_Length, m_Widthand m_Heightin the initialization list In the secondinstance, you are calling the constructor for CBox This causes the specific CBoxconstructor you havechosen to be called before the CCandyBoxconstructor is executed

If you build and run this example, it produces the output shown as follows:

CBox constructor calledCBox constructor calledCCandyBox constructor1 calledCBox constructor calledCCandyBox constructor2 calledmyBox occupies 24 bytesmyCandyBox occupies 32 bytesmyMintBox occupies 32 bytesmyMintBox volume is 6The calls to the constructors are explained in the following table:

The first line of output is due to the CBoxclass constructor call, originating from the declaration of theCBoxobject, myBox The second line of output arises from the automatic call of the base class constructorcaused by the declaration of the CCandyBoxobject myCandyBox

Notice how the base class constructor is always called before the derived class constructor.

The following line is due to your version of the default derived class constructor being called for themyCandyBoxobject This constructor is invoked because the object is not initialized The fourth line ofoutput arises from the explicit identification of the CBoxclass constructor to be called in our new con-structor for CCandyBoxobjects The argument values specified for the dimensions of the CCandyBoxobject are passed to the base class constructor Next comes the output from the new derived class con-structor itself, so constructors are again called for the base class first, followed by the derived class

Trang 39

It should be clear from what you have seen up to now that when a derived class constructor is executed,

a base class constructor is always called to construct the base part of the derived class object If you don’tspecify the base class constructor to be used, the compiler arranges for the default base class constructor

to be called

The last line in the table shows that the initialization of the base part of the myMintBoxobject is working

as it should be, with the privatemembers having been initialized by the CBoxclass constructor.Having the privatemembers of a base class only accessible to function members of the base class isn’talways convenient There will be many instances where you want to have privatemembers of a base

class that can be accessed from within the derived class As you surely have anticipated by now, C++

provides a way to do this

Declaring Class Members to be Protected

In addition to the publicand privateaccess specifiers for members of a class, you can also declaremembers of a class as protected Within the class, the protectedkeyword has the same effect as theprivatekeyword: members of a class that are protectedcan only be accessed by member functions ofthe class, and by friend functions of the class (also by member functions of a class that is declared as afriendof the class — you will learn about friend classes later in this chapter) Using the protectedkeyword, you could redefine the CBoxclass as follows:

Trang 40

Try It Out Using Protected Members

You can demonstrate the use of protecteddata members by using this version of the class CBoxtoderive a new version of the class CCandyBox, which accesses the members of the base class through itsown member function, Volume():

:CBox(lv, wv, hv) // Constructor{

cout << endl <<”CCandyBox constructor2 called”;

m_Contents = new char[ strlen(str) + 1 ];

strcpy_s(m_Contents, strlen(str) + 1, str);

}// Constructor to set contents// calls default CBox constructor automaticallyCCandyBox(char* str = “Candy”) // Constructor{

cout << endl << “CCandyBox constructor1 called”;

m_Contents = new char[ strlen(str) + 1 ];

strcpy_s(m_Contents, strlen(str) + 1, str);

}

~CCandyBox() // Destructor{

cout << “CCandyBox destructor called” << endl;

delete[] m_Contents;

}};

The code for main()in Ex9_04.cppis:

// Ex9_04.cpp// Using the protected access specifier

#include <iostream> // For stream I/O

Ngày đăng: 13/08/2014, 18:20