2. Objects and Classes
Objective: We'll show
Reference: Weiss, Chapter Two.
#ifndef _IntCell_H_
#define _IntCell_H_
// A class for simulating an integer memory cell.
//
// File: IntCell.h
//
class IntCell
{
public:
// Constructor. Initial value is initialValue.
explicit IntCell(int initialValue = 0)
: storedValue(initialValue) {}
// Copy constructor.
IntCell(const IntCell& rhs)
: storedValue(rhs.storedValue) {}
// Destructor.
~IntCell() {}
const IntCell& operator=(const IntCell& rhs);
// Accessor (or inspector) method.
int read() const { return storedValue; }
// Mutator method.
void write(int x) { storedValue = x; }
private:
int storedValue;
};
#endif
//
// File: IntCell.cpp
//
const IntCell& IntCell::operator=(const IntCell& rhs)
{
if (this != &rhs) // standard alias test
{
storedValue = rhs.storedValue;
}
return *this;
}
IntCell B = C;
IntCell B(C);
but not
B = C; // assignment
IntCell obj1 = 37;
#include "intCell.h"
int main()
{
IntCell m; // or, IntCell m(0); but not IntCell m();
m.write(5);
cout << "Cell contents: " << m.read() << endl;
return 0;
}
class IntCell
{
public:
explicit IntCell(int initialValue = 0)
{ storedValue = new int(initialValue); }
int read() const
{ return *storedValue; }
void write(int x)
{ *storedValue = x; }
private:
int *storedValue;
};
int f()
{
IntCell a(2);
IntCell b = a;
IntCell c;
c = b;
a.write(4);
cout << a.read() << endl << b.read()
<< endl << c.read << endl;
return 0;
}
#include "Rational.h"
#include <iostream>
using namespace std;
int main()
{
Rational x;
Rational sum = 0;
Rational max = 0;
int n = 0;
cout << "Type as many rationals as you want" << endl;
while (cin >> x)
{
cout << "Read " << x << endl;
sum += x;
if (x > max)
{
max = x;
}
n++;
}
cout << "Read " << n << rationals << endl;
if (max > IntType(0))
{
cout << "Largest number is " << max << endl;
}
if (n > 0)
{
cout << "Average is " << sum/IntType(n) << endl;
}
return 0;
}
//
// File: Rational.h
//
#include <iostream>
using namespace std;
typedef long IntType;
class Rational
{
public:
Rational(const IntType& numerator = 0)
: numer(numerator), denom(1) {}
Rational(const IntType& numerator, const IntType& denominator)
: numer(numerator), denom(denominator)
{ fixSigns(); reduce(); }
Rational(const Rational& rhs) : numer(rhs.numer), denom(rhs.denom) {}
~Rational() {}
const Rational& operator= (const Rational& rhs);
const Rational& operator+=(const Rational& rhs);
const Rational& operator-=(const Rational& rhs);
const Rational& operator*=(const Rational& rhs);
const Rational& operator/=(const Rational& rhs);
Rational operator+(const Rational& rhs) const;
Rational operator-(const Rational& rhs) const;
Rational operator*(const Rational& rhs) const;
Rational operator/(const Rational& rhs) const;
bool operator< (const Rational& rhs) const;
bool operator<=(const Rational& rhs) const;
bool operator> (const Rational& rhs) const;
bool operator>=(const Rational& rhs) const;
bool operator==(const Rational& rhs) const;
bool operator!=(const Rational& rhs) const;
const Rational& operator++(); // prefix
Rational operator++(int); // postfix
const Rational& operator--(); // prefix
Rational operator--(int); // postfix
const Rational& operator+() const; // unary +
Rational operator-() const; // unary -
bool operator!() const;
double toDouble() const
{ return static_cast< double >(numer) / denom; }
friend ostream& operator<<(ostream& out, const Rational& value);
friend istream& operator>>(ostream& in, Rational& value);
private:
IntType numer;
IntType denom;
void fixSigns(); // ensure denom >= 0
void reduce(); // ensure lowest form
};
void Rational::fixSigns()
{
if (denom < 0)
{
denom *= -1;
numer *= -1;
}
}
void Rational::reduce()
{
IntType d = 1;
if (denom != 0 && numer != 0)
{
if ((d = gcd(numer, denom)) > 1)
{
numer /= d;
denom /= d;
}
}
}
const Rational& Rational::operator=(const Rational& rhs)
{
if (this != &rhs)
{
numer = rhs.numer;
denom = rhs.denom;
}
return *this;
}
const Rational& Rational::operator+=(const Rational& rhs)
{
numer = numer * rhs.denom + rhs.numer * denom;
denom = denom * rhs.denom;
reduce();
return *this;
}
Rational Rational::operator+(const Rational& rhs) const
{
Rational answer(*this);
answer += rhs;
return answer;
}
bool Rational::operator==(const Rational& rhs) const
{
return numer * rhs.denom == denom * rhs.numer;
}
const Rational& Rational::operator++() // prefix
{
numer += denom;
return *this;
}
Rational Rational::operator++(int) // postfix
{
Rational tmp = *this;
numer += denom;
return tmp;
}
bool Rational::operator!() const
{
return !numer;
}
const Rational& Rational::operator+() const
{
return *this;
}
Rational Rational::operator-() const
{
return Rational(-numer, denom);
}
istream& operator>>(istream& in, Rational& value)
{
in >> value.numer;
value.denom = 1;
char ch;
in.get(ch);
if (!in.eof())
{
if (ch == '/')
{
in >> value.denom;
value.fixSigns();
value.reduce();
}
else
{
in.putback(ch); // unread ch
}
}
return in;
}
ostream& operator<<(ostream& out, const Rational& value)
{
if (value.denom != 0)
{
out << value.numer;
if (value.denom != 1)
out << '/' << value.denom;
return out;
}
// messy code for denom == 0
if (value.numer == 0)
{
out << "Indeterminate";
}
else
{
if (value.numer < 0)
{
out << '-';
}
out << "Infinity";
}
return out;
}
istream& operator>>(istream& in, IntCell& val)
{
int x;
in >> x;
if (!in.fail())
{
val = IntCell(x);
}
return in;
}
ostream& operator<<(ostream& out, const IntCell& x)
{
x.print(out);
return out;
}
where
void print(ostream& out = cout) const;
outputs in exactly the format required by operator<<.
public:
static int getActiveInstances() const
{ return activeInstances; }
private:
static int activeInstances;
We can then increment activeInstances in the constructor and decrement it in the destructor. We need to initialize the static data member where we place definitions of gloabl objects like:
int Rational::activeInstances = 0;
A static member function is invoked directly without a host object. For example,
cout << Rational::getActiveInstances() << endl;
public:
enum { RED = 0, BLACK = 1 };
#ifndef _MY_STRING_H_
#define _MY_STRING_H_
#include <iostream>
using namespace std;
class string
{
public:
string(char ch);
string(const char *cstring = "");
string(const string& str);
~string() { delete [] buffer; }
const string& operator= (const string& rhs);
const string& operator+=(const string& rhs);
const char *c_str() const { return buffer; }
int length() const {return strLength; }
char operator[](int k) const; // accessor
char& operator[](int k); // mutator
private:
int strLength; // length of string
int bufferLength; // capacity of buffer
char *buffer; // storage for characters
};
ostream& operator<<(ostream& out, const string& str);
istream& operator>>(istream& in, string& str);
istream& getline(istream& in, string& str,
char delim = '\n');
bool operator==(const string& lhs, const string& rhs);
bool operator!=(const string& lhs, const string& rhs);
bool operator< (const string& lhs, const string& rhs);
bool operator<=(const string& lhs, const string& rhs);
bool operator> (const string& lhs, const string& rhs);
bool operator>=(const string& lhs, const string& rhs);
#endif
#include <cstring>
#include "mystring.h"
string::string(const char *cstring)
{
if (cstring == NULL)
{
cstring = "";
}
strlength = strlen(cstring);
bufferLength = strLength + 1;
buffer = new char[bufferLength];
strcpy(buffer, cstring);
}
string::string(char ch)
{
strLength = 1;
bufferLength = 2;
buffer = new char[2];
buffer[0] = ch;
buffer[1] = '\0';
}
string::string(const string &str)
{
strLength = str.length();
bufferLength = strLength + 1;
buffer = new char[bufferLength];
strcpy(buffer, str.buffer);
}
const string& string::operator=(const string& rhs)
{
if (this != &rhs)
{
if (bufferLength < rhs.length() + 1)
{
delete [] buffer;
bufferLength = rhs.length() + 1;
buffer = new char[bufferLength + 1];
}
strLength = rhs.length();
strcpy(buffer, rhs.buffer);
}
return *this;
}
const string& string::operator+=(const string& rhs)
{
if (this == &rhs) // alias test; important!
{
string copy(rhs);
return *this += copy;
}
int newLength = length() + rhs.length();
if (newLength >= bufferLength)
{
bufferLength = 2 * (newLength + 1);
char *oldBuffer = buffer;
buffer = new char[bufferLength];
strcpy(buffer, oldBuffer);
delete [] oldBuffer;
}
strcpy(buffer + length(), rhs.buffer);
strLength = newLength;
return *this;
}
char& string::operator[](int k)
{
if (k < 0 || k >= strLength)
{
throw StringIndexOutOfBoundException();
}
return buffer[k];
}
char string::operator[](int k) const
{
if (k < 0 || k >= strLength)
{
throw StringIndexOutOfBoundException();
}
return buffer[k];
}
ostream& operator<<(ostream& out, const string& str)
{
return out << str.c_str();
}
istream& operator>>(istream& in, string& str)
{
char ch;
str = "";
if (in >> ch)
{
do
{
str += ch;
in.get(ch);
} while(!in.fail() && !isspace(ch));
if (isspace(ch))
{
in.putback(ch);
}
}
return in;
}
istream& getline(istream& in, string& str, char delim)
{
char ch;
str = "";
while (in.get(ch) && ch != delim)
str += ch;
return in;
}
bool operator==(const string& lhs, const string& rhs)
{
return strcmp(lhs.c_str(), rhs.c_str()) == 0;
}
bool operator!=(const string& lhs, const string& rhs)
{
return strcmp(lhs.c_str(), rhs.c_str()) != 0;
}
bool operator<(const string& lhs, const string& rhs)
{
return strcmp(lhs.c_str(), rhs.c_str()) < 0;
}
bool operator<=(const string& lhs, const string& rhs)
{
return strcmp(lhs.c_str(), rhs.c_str()) <= 0;
}
bool operator>(const string& lhs, const string& rhs)
{
return strcmp(lhs.c_str(), rhs.c_str()) > 0;
}
bool operator>=(const string& lhs, const string& rhs)
{
return strcmp(lhs.c_str(), rhs.c_str()) >= 0;
}
void reverseCopy(const string& from, string& to)
{
int len = from.size();
to = from;
for (int i = 0; i < len; i++)
{
to[i] = from[len -1 -i];
}
}
bool operation>=(const char *lhs, const string& rhs); bool operation>=(const string& lhs, const char *rhs);
and class member functions
const string& operation=(const char *rhs); const string& operation+=(const char *rhs); const string& operator+=(char ch)
throw StringIndexOutOfBoundException();
in the implementations of operator[] is a kind of exception handling which will be discussed later.