
Beginning Visual C++ 2005 (2006) [eng]-1
.pdf
More on Classes
Function Definitions |
Class Definition |
|
|
Function Definitions |
|
|
|
|
|
|
Class Definition |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Function Definitions |
|
|
|
|
|
|
Global Constants |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Definition of main() |
|
|
|
|
|
|
Global Constants |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Source files |
Header files |
with the extension .cpp |
with the extension .h |
Figure 8-14
From time to time, you might want to use code from existing files in a new project. In this case you only have to add the .cpp files to the project, which you can do by using the Project > Add Existing Item menu option, or by right-clicking either Source Files or Header Files in the Solution Explorer tab and selecting Add > Existing Item from the context menu to add the file to your project. You don’t need to add .h files to your project, although you can if you want them to be shown in the Solution Explorer pane immediately. The code from .h files is added at the beginning of the .cpp files that require them as a result of the #include directives that you specify. You need #include directives for header files containing standard library functions and other standard definitions, as well as for your own header files. Visual C++ 2005 automatically keeps track of all these files, and enables you to view them in the Solution Explorer tab. As you saw in the last example, you can also view the class definitions and global constants and variables in the Class View tab.
459

Chapter 8
In a Windows program, there are other kinds of definitions for the specification of things such as menus and toolbar buttons. These are stored in files with extensions like .rc and .ico. Just like .h files, these do not need to be explicitly added to a project because they are created and tracked automatically by Visual 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 .h file with a filename based on the class name, and to store the implementation of the function members of the class that are defined outside the class definition in a .cpp file with the same name. On this basis, the definition of our CBox class appeared in a file with the name Box.h. Similarly, the class implementation was stored in the file Box.cpp. We didn’t follow this convention in the earlier examples in the chapter because the examples were very short, and it was easier to reference the examples with names derived from the chapter number and the sequence number of the example within the chapter. With programs of any size though it becomes essential to structure the code in this way, so it would be a good idea to get into the habit of creating .h and .cpp files to hold your program code from now on.
Segmenting a C++ program into .h and .cpp files is a very convenient approach, as it makes it easy for you to find the definition or implementation of any class, particularly if you’re working in a development environment that doesn’t have all the tools that Visual C++ provides. As long as you know the class name, you can go directly to the file you want. This isn’t a rigid rule, however. It’s sometimes useful to group the definitions of a set of closely related classes together in a single file and assemble their implementations similarly; however you choose to structure your files, the Class View still displays all the individual classes, as well as all the members of each class, as you can see in Figure 8-15.
Figure 8-15
460

More on Classes
I adjusted the size of the Class View pane so all the elements in the project are visible. Here, you can see the details of the classes and globals for the last example. As mentioned before, double-clicking any of the 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 reference classes in the next chapter. You can also call delete for a handle to a reference class, but again, this is not normally 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 to explore. 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 that you must not overload the assignment operator in your value classes because the process for the assignment of one value class object to another of the same type is already defined to be member-by-member copying 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 objects then you must implement the appropriate function. Another difference from native C++ classes is that functions that implement operator overloading in C++/CLI classes can be static members of a class as well as instance members. This means that you have the option of implementing binary operators in C++/CLI classes with static member functions with two parameters in addition to the possibilities you have 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 possibility to implement a prefix unary operator as a static member function with no parameters. Finally, although in native C++ you can overload the new operator, you cannot overload the gcnew operator in a C++/CLI class.
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 operator overloading can be implemented for a value class. Addition seems like a good place to start, so here’s the Length value class, complete with the addition operator function:
value class Length |
|
{ |
|
private: |
|
int feet; |
// Feet component |
int inches; |
// Inches component |
public:
static initonly int inchesPerFoot = 12;
// Constructor
Length(int ft, int ins) : feet(ft), inches(ins){ }
// A length as a string
461

Chapter 8
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, inchesPerFoot is static so it is directly available to static and non-static function members of the class. Declaring inchesPerFoot as initonly means that it cannot be modified so it can be a public member of the class. There’s a ToString() function override defined for the class so you can write Length objects to the command line using the Console::WriteLine() function. The operator+() function implementation is very simple. The function returns a new Length object produced by combining the feet and inches component for the current object and the parameter, len. The calculation is done by combining the two lengths in inches and then computing the arguments to the Length class constructor for the new object from the value for the combined lengths in inches.
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 Length objects so this invokes the operator+() function. The result is a new Length object for which the compiler arranges to call the ToString() 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 Length class, like this:
static Length operator+(Length len1, Length len2)
{
int inchTotal = len1.inches+len2.inches+inchesPerFoot*(len1.feet+len2.feet);
return Length(inchTotal/inchesPerFoot, inchTotal%inchesPerFoot);
}
}
The parameters are the two Length objects to be added together to produce a new Length object. Because this is a static member of the class, the operator+() function is fully entitled to access the private members, feet and inches, of both the Length objects passed as arguments. Friend functions are not allowed in C++/CLI classes and an external function would not have access to private members of the class so you have no other possibilities for implementing the addition operator.
462

More on Classes
Because we are not working with areas multiplication for Length objects really only makes sense multiplying a length by a numerical value. You can implement this as a static member of the class, but let’s define the function outside the class. The class looks like this:
value class Length
{
private: int feet;
int inches;
public:
static initonly int inchesPerFoot = 12;
// Constructor
Length(int ft, int ins) : feet(ft), inches(ins){ }
// A length as a string
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 value
static 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 preand postmultiply a Length object by a value of type double. The definition of the operator*() function outside the class for pre-multiplication is:
Length Length::operator *(double x, 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 functions with 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);
463

Chapter 8
Both lines of output from this code fragment should reflect the same result from multiplication(19 feet 2 inches. The argument expression factor*len2 is equivalent to:
Length::operator*(factor, len2).ToString()
The result of calling the static operator*() function is a new Length object and the ToString() function for that is called to produce the argument to the WriteLine() function. The expression len2*factor is similar but calls the operator*() function that has the parameters reversed. Although the operator*() functions have been written to deal with a multiplier of type double, they also work with integers. The compiler automatically promotes an integer values to type double when you use it in an expression such as 12*(len1+len2).
We could expand a little further on overloaded operators in the Length class 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 Length class:
//Ex8_09.cpp : main project file.
//Overloading operators in the value class, Length #include “stdafx.h”
using namespace System;
value class Length
{
private: int feet;
int inches;
public:
static initonly int inchesPerFoot = 12;
// Constructor
Length(int ft, int ins) : feet(ft), inches(ins){ }
// A length as a string
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);
464

More on Classes
}
static Length operator*(double x, Length len); // Pre-multiply by a double value static 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 inches 2.5 times 7 feet 8 inches is 19 feet 2 inches
7 feet 8 inches times 2.5 is 19 feet 2 inches
The sum of 6 feet 9 inches and 7 feet 8 inches divided by 2.5 is 5 feet 9 inches Press any key to continue . . .
How It Works
The new operator overloading function in the Length class is for division and it allows division of a Length value by a value of type double. Dividing a double value by a Length object does not have an obvious meaning so there’s no need to implement this version. The operator/() function is implemented as another static member of the class and the definition appears within the body of the class definition to contrast how that looks compared with the operator*() functions. You would normally define 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);
}
465

Chapter 8
It now has one argument that is the right operand for the / operator. The left operand is the current object referenced by the this pointer (implicitly in this case).
The operators are exercised in the four output statements. Only the last one is new to you and this combines the use of the overloaded + operator for Length objects with the overloaded / operator. The last argument to the Console::WriteLine() function in the fourth output statement is (len1+len2)/ factor which is equivalent to the expression:
Length::operator/(len1.operator+(len2), factor) .ToString()
The first argument to the static operator/() function is the Length object that is returned by the operator+() function, and the second argument is the factor variable, which is the divisor. The ToString() function for the Length object returned by operator/() is called to produce the argument string that is passed to the Console::WriteLine() function.
It is possible that you might want the capability to divide one Length object by another and have a value of type int as the result. This would allow you to figure out how many 17 inch lengths you can cut from 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 left over. 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 len1 by len2 and return it as a new Length object.
With all these operators you really can use your Length objects in arithmetic expressions. You can write statements 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 total will be 275 feet 9 inches. The last statement makes use of the assignment operator that comes with every value class as well as the operator*(), operator+(), and operator/() functions in the Length class. This operator overloading is not only powerful stuff, it really is easy isn’t it?
466

More on Classes
Overloading the Increment and Decrement Operators
Overloading the increment and decrement is simpler in C++/CLI that in native C++. As long as you implement the operator function as a static class member, the same function will serve as both the prefix and postfix operator functions. Here’s how you could implement the increment operator for the Length class:
class Length
{
public:
//Code as before...
//Overloaded increment operator function - increment by 1 inch static Length operator++(Length len)
{
++len.inches;
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 code exercises the function:
Length len = Length(1, 11); |
// 1 foot 11 inches |
Console::WriteLine(len++); |
|
Console::WriteLine(++len); |
|
Executing this fragment produces the output:
1 feet 11 inches
2 feet 1 inches
Thus the prefix and postfix increment operations are working as they should using a single operator function in the Length class. This occurs because the compiler is able to determine whether to use the value of the operand in a surrounding expression before or after the operand has been incremented and compile the code accordingly.
Overloading Operators in Reference Classes
Overloading operators in a reference class is essentially the same as overloading operators in a value class, the primary difference being that parameters and return values are typically handles. Let’s see how the Length class 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 Length as a reference class with the same set of overloaded operators as the value class version:
467

Chapter 8
//Ex8_10.cpp : main project file.
//Defining and using overloaded operator
#include “stdafx.h” using namespace System;
ref class Length
{
private: int feet;
int inches;
public:
static initonly int inchesPerFoot = 12;
// Constructor
Length(int ft, int ins) : feet(ft), inches(ins){ }
// A length as a string
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 double static Length^ operator*(Length^ len, double x); // Multiply - L operand double
// Preand postfix increment operator
468