11.5.3 An implementation comment
) The separation of interface from implementation is one of the goals of OOP.
) Example of the implementation of the class Vector in a rand walk simulation:
// randwalk.cpp -- using the Vector class
// compile with the vect.cpp filebuf
#include <iostream>
#include <cstdlib>
#include <ctime>
#include "vect.h"
int main()
{
using namespace std;
using VECTOR::Vector;
srand(time(0)); // seed random-number generator
double direction;
Vector step;
Vector result(0.0, 0.0);
unsigned long steps = 0;
double target;
double dstep;
cout << "Enter target distance (q to quit): ";
while (cin >> target)
{
cout << "Enter step length: ";
if (!(cin >> dstep))
break;
while(result.magval() < target)
{
direction = rand() % 360;
step.reset(dstep, direction, Vector::POL);
result = result + step;
steps++;
}
cout << "After " << steps << " steps, the subject has the following location:
";
cout << result << endl;
result.polar_mode();
cout << "or
" << result << endl;
cout << "Average outward distance per step = " << result.magval() / steps << endl;
steps = 0;
result.reset(0.0, 0.0);
cout << "Enter target distance (q to quit): ";
}
cout << "Bye!
";
cin.clear();
while (cin.get() != '
')
continue;
return 0;
}
Noteworthy:
1 The program uses "using VECTOR::Vector;"" to attain access to the Vector::POL enumeration for later use
2 C++ includes a rand() functiono to yield an integer range from 0 to implementation-dependent value. If you want smaller range, use rand() modulus the range you desired. Actually, the rand() is really pseudorandom because rand() works by applying an algorithm to an initial seed value to get a random value. So when you call the function consecutively for 10 times, it might yield 10 same numbers. To prevent the problem, use this code:
srand(time(0));
time(0) returns the current calendar time, which is based on seconds. Then srand() uses the time integer to set the initial seed, making the numbers more random. The cstdlib header file contains srand() and rand(), while ctime contains the time() prototype.
3 Incidentally, the following statement will set result automatically to form RECT:
result = result + step;
11.6 Automatic conversions and type casts for classes
) C++ does automatic type casts for built-in types. For user-defined types, one-argument constructor is taken as the type conversion:
class Stonewt
{
...
}
...
Stonewt::Stonewt(double lbs)
{
...
}
...
Stonewt myCat;
myCat = 19.6; // use Stonewt(double lbs) to automatically convert double to type Stonewt
) But this implicit conversion may lead to trouble, so C++ adds a keyword explicit to turn off the implicit conversion. You can use explicit in the constructor declaration, cutting of implicit conversions but still leaving explicit conversions valid:
Stonewt myCat;
myCat = 19.6; // invalid, implicit conversion is banned with keyword explicit
myCat = Stonewt(myCat); // ok, explicit conversion allowed
) Implicit typc conversion(such as double to Stonewt) may occur under certain circumstances:
1 initialize a Stonewt object to a type double value
2 assign a type double value to a Stonewt object
3 pass a type double value to a function that expects a Stonewt argument
4 a function which is declared to return Stonewt but trying to return double
5 When any preceding situations use a built-in type that can unambiguously be converted to double
) Example:
// stonewt.h -- definition for the Stonewt class
#ifndef STONEWT_H_INCLUDED
#define STONEWT_H_INCLUDED
class Stonewt
{
private:
static const int Lbs_per_stn = 14;
int stone;
double pds_left;
double pounds;
public:
Stonewt(double lbs);
Stonewt(int stn, double lbs);
Stonewt();
~Stonewt();
void show_lbs() const;
void show_stn() const;
};
#endif // STONEWT_H_INCLUDED
// stonewt.cpp -- Stonewt methods
#include <iostream>
#include "stonewt.h"
using std::cout;
Stonewt::Stonewt(double lbs)
{
stone = int(lbs) / Lbs_per_stn;
pds_left = int(lbs) % Lbs_per_stn + lbs - int(lbs);
pounds = lbs;
}
Stonewt::Stonewt(int stn, double lbs)
{
stone = stn;
pds_left = lbs;
pounds = stn * Lbs_per_stn + lbs;
}
Stonewt::Stonewt()
{
stone = pounds = pds_left = 0;
}
Stonewt::~Stonewt()
{
}
void Stonewt::show_stn() const
{
cout << stone << " stone, " << pds_left << " pounds
";
}
void Stonewt::show_lbs() const
{
cout << pounds << " pounds
";
}
// stonewt.cpp -- Stonewt methods
#include <iostream>
#include "stonewt.h"
using std::cout;
Stonewt::Stonewt(double lbs)
{
stone = int(lbs) / Lbs_per_stn;
pds_left = int(lbs) % Lbs_per_stn + lbs - int(lbs);
pounds = lbs;
}
Stonewt::Stonewt(int stn, double lbs)
{
stone = stn;
pds_left = lbs;
pounds = stn * Lbs_per_stn + lbs;
}
Stonewt::Stonewt()
{
stone = pounds = pds_left = 0;
}
Stonewt::~Stonewt()
{
}
void Stonewt::show_stn() const
{
cout << stone << " stone, " << pds_left << " pounds
";
}
void Stonewt::show_lbs() const
{
cout << pounds << " pounds
";
}
11.6.1 Conversion functions
) You could also convert user-defined types to built in types, using conversion functions in C++. The form of the function is operator typenName();:
operator int();
operator double():
) The conversion function must be a class method, ** does not specify return type** and has no arguments.
class thing
{
...
public:
operator int() const;
};
...
thing::operator int() const
{
return int(...);
}
) Examples:
// stonewt1.h -- revised definition for the Stonewt class
#ifndef STONEWT1_H_INCLUDED
#define STONEWT1_H_INCLUDED
class Stonewt
{
private:
static const int Lbs_per_stn = 14;
int stone;
double pds_left;
double pounds;
public:
Stonewt(double lbs);
Stonewt(int stn, double lbs);
Stonewt();
~Stonewt();
void show_lbs() const;
void show_stn() const;
operator int() const;
operator double() const;
};
#endif // STONEWT1_H_INCLUDED
// stonewt1.cpp -- Stonewt methods + conversion functions
#include <iostream>
#include "stonewt1.h"
using std::cout;
Stonewt::Stonewt(double lbs)
{
stone = int(lbs) / Lbs_per_stn;
pds_left = int(lbs) % Lbs_per_stn + lbs - int(lbs);
pounds = lbs;
}
Stonewt::Stonewt(int stn, double lbs)
{
stone = stn;
pds_left = lbs;
pounds = stn * Lbs_per_stn + lbs;
}
Stonewt::Stonewt()
{
stone = pounds = pds_left = 0;
}
Stonewt::~Stonewt()
{
}
void Stonewt::show_stn() const
{
cout << stone << " stone, " << pds_left << " pounds
";
}
void Stonewt::show_lbs() const
{
cout << pounds << " pounds
";
}
Stonewt::operator int() const
{
return int(pounds + 0.5);
}
Stonewt::operator double() const
{
return pounds;
}
// stone1.cpp -- user-defined conversion functions
// compile with stonewt1.cpp
#include <iostream>
#include "stonewt1.h"
int main()
{
using std::cout;
Stonewt poppins(9, 2.8);
double p_wt = poppins;
cout << "Convert to double => ";
cout << "Poppins: " << p_wt << " pounds.
";
cout << "Convert to int => ";
cout << "Poppins: " << int(poppins) << " pounds.
";
return 0;
}
) Noteworthy:
1 Using cout with type casts may produce ambiguity:
cout << poppins << endl;
The compiler doesn't know whether to convert poppins to double or to int, which are both defined conversions, so it raises an ambiguous error
2 Similar problem comes with assignment:
long gone = poppins;
Both double and int could be converted to long, so the compiler doesn't know whether to convert poppins to double or int first, later to long, so it raises an error. If only one conversion is defined, the compiler would accept it
) The moral is to use explicit conversions to exclude the possibility of implicit conversions.
11.6.2 Conversions and Friends
) As mentioned earlier, you could use either a member function or a friend function to overload addition. See following examples:
NO.1
Stonewt jennySt(9, 12);
Stonewt bennySt(12, 8);
Stonewt total;
total = jennySt + benntSt;
the addition sentence could be matched with both member and friend forms:
total = jennySt.operator+(bennySt);
total = operator+(jennySt, bennySt);
NO.2
Stonewt jennySt(9,12);
double kennyD = 176.0;
Stonewt total;
total = jennySt + kennyD;
the addition sentence could be matched with both member and friend forms:
total = jennySt.operator+(kennyD); // here kennyD is converted to class Stonewt because the operator+() wants a Stonewt and the conversion is valid
total = operator+(jennySt, kennyD); // same, converting kennyD to Stonewt as a function parameter
NO.3(!)
Stonewt jennySt(9,12);
double pennyD = 146.0;
Stonewt total;
total = pennyD + jennySt;
The addition line could only be performed using the friend form:
total = pennyD.operator+(jennySt); // INVALID! pennyD is type double thus could not invoke operator+() method
total = operator+(pennyD, jennySt); // valid, pennyD is converted to Stonewt as function argument
Remember, conversion takes place for member function arguments, not for member function invokers.
) Choices in implementing addition
NO.1 Use a friend function and have the constructor to handle conversion of double to Stonewt:
operator+(const Stonewt &, const Stonewt &);
NO.2 Overload the addition operator with functions that explicitly use type double argument:
Stonewt operator+(Stonewt & s); // member function
friend Stonewt operator+(double x, Stonewt & s);
The first program is shorter but consumes more time, the second is longer but runs faster