Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C++ For Mathematicians (2006) [eng]

.pdf
Скачиваний:
194
Добавлен:
16.08.2013
Размер:
31.64 Mб
Скачать

456

C++ for Mathematicians

bool operator<(const mycomplex& that) const { if (real() < that.real()) return true;

if (real() > that.real()) return false; if (imag() < that.imag()) return true; return false;

}

};

#endif

We define the three constructors by passing arguments up to the constructor for complex<double>. No further action is required by these constructors and so the bodies of these three constructors are empty: {}.

Next the operator < is defined. Note that we use the complex<double> methods real() and imag() to access the data, and then use lexicographic ordering.

Here is a short main to test the mycomplex class.

#include "mycomplex.h" #include <iostream> #include <set>

using namespace std;

int main() { mycomplex z; mycomplex w(2); mycomplex v(-5,2);

set<mycomplex> S; S.insert(z); S.insert(w); S.insert(v);

set<mycomplex>::iterator si;

for (si = S.begin(); si != S.end(); ++si) { cout << *si << " ";

}

cout << endl;

return 0;

}

This produces the following output.

 

 

 

 

 

 

 

(-5,2) (0,0) (2,0)

 

 

 

 

 

10.4The difficulty here is that each class depends on the other, leaving us in a quandary as to which to define first. In C++ one must declare classes and variables before they can be used. So, the solution is to give a preliminary declaration of the Segment class before giving a full declaration of Point. This is done with the statement class Segment; in the header file.

Answers

457

The operator+ in Point uses the Segment(Point,Point) constructor to convert a pair of points into a line segment. We cannot put the code for operator+ inline in the declaration of the Point class because the required Segment(double,double) constructor has not yet been declared. So we are compelled to write the code for operator+ in a separate .cc file.

Here are the files pointseg.h and pointseg.cc.

#ifndef POINTSEG_H #define POINTSEG_H

#include <iostream> using namespace std;

class Segment;

class Point { private:

double x,y; public:

Point() { x = y = 0; } Point(double xx, double yy) {

x = xx; y = yy;

}

double getX() const { return x; } double getY() const { return y; }

Segment operator+(const Point& that) const;

};

class Segment { private:

Point A,B; public:

Segment() { A = Point(0,0); B = Point(1,0); } Segment(Point X, Point Y) { A = X; B = Y; } Point getA() const { return A; }

Point getB() const { return B; } Point midpoint() const;

};

ostream& operator<<(ostream& os, const Point& P); ostream& operator<<(ostream& os, const Segment& S);

#endif

#include "pointseg.h"

Segment Point::operator+(const Point& that) const { return Segment(*this, that);

}

Point Segment::midpoint() const { double x = (A.getX() + B.getX()) / 2; double y = (A.getY() + B.getY()) / 2; return Point(x,y);

458

C++ for Mathematicians

}

ostream& operator<<(ostream& os, const Point& P) { os << "(" << P.getX() << "," << P.getY() << ")"; return os;

}

ostream& operator<<(ostream& os, const Segment& S) { os << "[" << S.getA() << "," << S.getB() << "]"; return os;

}

10.5No. Suppose that class Alpha declares a data member of type Beta and vice versa. If this were allowed, we could create an infinite nesting: Alpha objects contain Beta objects which in turn contain Alpha objects, ad infinitum.

Examine this header file and the problem should become clear. class Beta;

class Alpha { private:

Beta b;

};

class Beta { private:

Alpha a;

};

 

Trying to #include this header file into a program generates a compiler error

 

 

message like this.

 

 

 

 

In file included from alphabeta.cc:1:

 

 

 

 

alphabeta.h:5: error: field ‘b’ has incomplete type

 

 

 

10.6The key idea is to derive Complexx as a subclass of complex<double> and add two Boolean data fields to signal whether the object is infinite and whether the object is invalid. To save some typing, we typedef the symbol C to be shorthand for complex<double>.

To define the arithmetic operators, we rely on the corresponding operators from complex. To convert an object z of type Complexx to its parent class C, we use the expression C(z). So, to multiple Complexx objects w and z, we first check for special cases where either is infinite or invalid, and then to get the required product we may use the expression C(w)*C(z).

Here are the files Complexx.h and Complexx.cc. To make this class more useful, we should add the operators +=, -=, *=, and /=. We should also extend all the arithmetic operators so that one of the arguments can be a double.

#include <complex> using namespace std;

Answers

459

typedef double R; typedef complex<R> C;

class Complexx : public C { private:

bool infinite; bool invalid;

public:

Complexx(C z) : C(z) { infinite = false; invalid = false;

}

Complexx(R a, R b) : C(a,b) { infinite = false;

invalid = false;

}

bool isZero() const {

return !invalid && !infinite && C(*this) == C(0);

}

bool isInfinite() const { return !invalid && infinite;

}

bool isInvalid() const { return invalid; }

bool operator==(const Complexx& that) const { if (invalid || that.invalid) return false; if (infinite && that.infinite) return true; return C(*this) == C(that);

}

bool operator!=(const Complexx& that) const { if (invalid || that.invalid) return false; return !(*this == that);

}

Complexx operator+(const Complexx& that) const; Complexx operator-() const;

Complexx operator-(const Complexx& that) const; Complexx operator*(const Complexx& that) const; Complexx operator/(const Complexx& that) const;

};

ostream& operator<<(ostream& os, const Complexx& z);

#include "Complexx.h"

Complexx Complexx::operator+(const Complexx& that) const { Complexx ans(C(*this) + C(that));

if (invalid || that.invalid) { ans.invalid = true;

return ans;

}

if (infinite && that.infinite) { ans.invalid = true;

460

C++ for Mathematicians

return ans;

}

if (infinite || that.infinite) { ans.infinite = true;

return ans;

}

return ans;

}

Complexx Complexx::operator-() const { Complexx ans(-C(*this));

if (invalid) { ans.invalid = true; return ans;

}

if (infinite) { ans.infinite = true; return ans;

}

return ans;

}

Complexx Complexx::operator-(const Complexx& that) const { return (*this) + (-that);

}

Complexx Complexx::operator*(const Complexx& that) const { Complexx ans(C(*this) * C(that));

if (invalid || that.invalid) { ans.invalid = true;

return ans;

}

if (infinite && that.isZero()) { ans.invalid = true;

return ans;

}

if (isZero() && that.infinite) { ans.invalid = true;

return ans;

}

if (infinite || that.infinite) { ans.infinite = true;

return ans;

}

return ans;

}

Complexx Complexx::operator/(const Complexx& that) const { Complexx ans(C(*this) / C(that));

if (invalid || that.invalid) { ans.invalid = true;

return ans;

}

if (isZero() && that.isZero()) { ans.invalid = true;

return ans;

Answers

461

}

if (infinite && that.infinite) { ans.invalid = true;

return ans;

}

if (infinite) { ans.infinite = true; return ans;

}

if (that.infinite) {

ans = Complexx(0.,0.); return ans;

}

if (that.isZero()) { ans.infinite = true; return ans;

}

return ans;

}

ostream& operator<<(ostream& os, const Complexx& z) { if (z.isInvalid()) {

os << "INVALID"; return os;

}

if (z.isInfinite()) { os << "Infinity"; return os;

}

os << C(z); return os;

}

11.1Here is a hint. Suppose that π is written in disjoint cycle notation. The order

of π is the least common multiple of lengths of the cycles. Also for positive integers a and b, the least common multiple of a and b is ab/gcd(a,b).

11.2Here is the file Counted.h. Notice how we use the constructor just to increment the variable n_objects and the destructor to decrement it.

#ifndef COUNTED_H #define COUNTED_H

class Counted { private:

static long n_objects;

public:

Counted() { n_objects++; } ˜Counted() { n_objects--; }

long static count() { return n_objects; }

};

#endif

462

C++ for Mathematicians

This header file contains nearly everything except the code to initialize the static class variable n_objects. This is done in a separate source file,

Counted.cc:

#include "Counted.h"

long Counted::n_objects = 0;

11.3 Here are the files Partition.h and Partition.cc.

#ifndef PARTITION_H #define PARTITION_H #include <iostream> using namespace std;

class Partition { private:

int sum;

int n_parts; int *parts;

public: Partition() {

sum = n_parts = 0; parts = new int[1];

}

Partition(const Partition& that) { sum = that.sum;

n_parts = that.n_parts; parts = new int[n_parts+1];

for (int k=0; k<n_parts; k++) parts[k] = that.parts[k];

}

˜Partition() { delete[] parts;

}

Partition operator=(const Partition& that) { sum = that.sum;

n_parts = that.n_parts; delete[] parts;

parts = new int[n_parts+1];

for (int k=0; k<n_parts; k++) parts[k] = that.parts[k]; return *this;

}

void add_part(int n);

int get_sum() const { return sum; } int nparts() const { return n_parts; } int operator[](int k) const;

bool operator<(const Partition& that) const;

};

ostream& operator<<(ostream& os, const Partition& P);

#endif

#include "Partition.h"

void Partition::add_part(int n) {

 

Answers

463

if (n <= 0) {

 

cerr <<

"Cannot add a negative part" << endl;

 

return;

 

 

}

sum += n;

int* new_parts = new int[n_parts+1]; for (int k=0; k<n_parts; k++) {

new_parts[k] = parts[k];

}

new_parts[n_parts] = n; n_parts++;

sort(new_parts, new_parts+n_parts); delete[] parts;

parts = new_parts;

}

int Partition::operator[](int k) const { if ((k<0) || (k>=n_parts)) {

cerr << "Index out of range" << endl; return -1;

}

return parts[k];

}

bool Partition::operator<(const Partition& that) const { if (sum < that.sum) return true;

if (sum > that.sum) return false;

if (n_parts < that.n_parts) return true; if (n_parts > that.n_parts) return false; for (int k=0; k<n_parts; k++) {

if (parts[k] < that.parts[k]) return true; if (parts[k] > that.parts[k]) return false;

}

return false;

}

ostream& operator<<(ostream& os, const Partition& P) { if (P.nparts() < 2) {

os << P.get_sum(); return os;

}

for (int k=0; k<P.nparts(); k++) { os << P[k];

if (k < P.nparts()-1) os << "+";

}

return os;

}

11.5 Here is the file SmartArray.h that implements the class.

#ifndef SMART_ARRAY_H #define SMART_ARRAY_H

class SmartArray { private:

long N;

464 C++ for Mathematicians

long* data;

long adjust_index(long k) { k %= N;

if (k<0) k += N; return k;

}

public:

SmartArray(long nels = 1) { N = (nels > 1) ? nels : 1; data = new long[N];

}

˜SmartArray() { delete[] data; } long get_N() const { return N; } long& operator[](long k) {

long kk = adjust_index(k); return data[kk];

}

};

#endif

Note that we define two private data elements: N (the size of the array) and data (a place to hold the data elements).

We also define a private method adjust_index that maps an array index to fall between 0 and N-1 (inclusive).

The constructor, after ensuring that its argument is at least 1, allocates storage for the data array. The destructor’s only job is to release the memory held by data. The get_N method is a convenient way to learn the size of the array. (Note: In this implementation we do not provide any way to resize an array.)

And now we come to the main point of this exercise: the operator[] method. Notice that we declared the return type of this method to be long&. If we had, instead, declared the return type to be long, then return data[kk]; would simply return a copy of the value held in slot kk of the data. This is just like call by value, but in this case, it’s return by value.

By declaring the return type to be long&, we switch from returning a copy of a value to returning the actual item itself; that is, we have implemented return by reference.

Suppose the following statements appear in a main(),

SmartArray X(10);

X[-1] = 4;

The first declares X to be a SmartArray housing 10 values. When the second statement is encountered, here’s what happens. First adjust_index(-1) runs and returns the value 9 (and that’s held in the temporary variable kk). Next the code executes return data[-1];. So the statement X[-1] = 4; is, effectively, transformed into X.data[9] = 4;.

Answers

465

11.6The following code is a file named LFT.h that defines the class LFT. Notice that we defined C to be an abbreviation for complex<double>. We include get methods to extract the coefficients of the transformation and an operator<< to print the transformations to the screen.

#ifndef LFT_H #define LFT_H #include <complex> #include <iostream> using namespace std;

typedef complex<double> C;

class LFT { private:

C a,b,c,d; public:

LFT(C aa, C bb, C cc, C dd) {

a= aa; b = bb; c = cc; d = dd;

}

LFT() {

a= 1; b = 0; c = 0; d = 1;

}

C getA() const { return a; } C getB() const { return b; } C getC() const { return c; } C getD() const { return d; } C operator()(C z) const {

return (a*z+b)/(c*z+d);

}

LFT operator*(const LFT& T) const { C aa,bb,cc,dd;

aa= a*T.a + b*T.c;

bb= a*T.b + b*T.d;

cc= c*T.a + d*T.c;

dd= c*T.b + d*T.d;

return LFT(aa,bb,cc,dd);

}

};

inline ostream& operator<<(ostream& os, const LFT& T) { os << "z |--> (" << T.getA() << "*z + " << T.getB()

<< ")/(" << T.getC() << "*z + " << T.getD() << ")";

return os;

}

#endif

11.7The files Path.h and Path.cc follow. Note that we used a vector container

to house the points. This is easier than managing a Path* array. As a bonus, because we do not need to delete[] any arrays we allocated, we do not need a ˜Path() destructor.

Also note that we did not define an operator+ for the case Path+Point. The C++ compiler knows how to convert a Point to a Path (thanks to the