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.