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

C++ For Mathematicians (2006) [eng]

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

406

C++ for Mathematicians

and in the .cc file we have

MyClass::object_count = 0;

One may also have static methods. These are class methods that do not apply to any particular object, but to the class as a whole. They can only access static data in the class because the other data are particular to each object of the class.

In the example above, we would want a method that reports how many objects of type MyClass currently exist. To that end, we add to the public section of MyClass the following method,

static int get_object_count() { return object_count; }

Because this method applies to the class MyClass and not to any particular object of that type, it is inappropriate to invoke it as A.get_object_count() (where A is of type MyClass). Rather, we write MyClass::get_object_count().

C.5.6 this

Class methods may access all data fields of the object on which they are invoked. On occasion, it is useful for a method to refer to the entire object on which it was invoked. For example, when we define ++A (by declaring an operator++), the conventional return value of this operator is the new value assigned to the object.

To do this, C++ provides a pointer named this. The this pointer refers to the object on which it is invoked. Suppose MyClass has a method named work. If we use the pointer this inside of work, then this is a pointer to the object A and *this is the object A itself.

So, when we define operator++() for MyClass, the final statement of that method would be return *this;.

Likewise, when we define an assignment operator, the return value of that operator should be the new value of the left-hand argument. Ending with return *this; accomplishes this task.

C.5.7 Friends

Private data elements of a class can be accessed by class methods, but not by other procedures. However, it is possible to grant a procedure special access to private data elements by declaring that procedure to be a friend of the class.

To do this, we need to declare the friend procedure inside the class declaration like this:

class MyClass { private:

...

public:

...

friend int buddy(MyClass Z);

};

In the .cc file we would see this:

C++ Reference

407

int buddy() {

// calculations using Z’s fields return answer;

}

Note that buddy is not a method of MyClass, so in the .cc file we do not use the prefix MyClass::.

It is generally safer not to allow any functions to have access to the data elements of a class. The use of friend procedures subverts the goal of data hiding. In other words: Don’t use this feature of C++.

Furthermore, it is possible to have a procedure that is a friend of two different classes, or for one class to be a friend of another. These advanced topics are beyond the scope of this text.

C.5.8 Class templates

C++ implements complex numbers using a class template. For complex numbers with double real and imaginary parts, we use the declaration complex<double> whereas for Gaussian integers we use complex<int>.

We create our own class templates like this:

template <class T> class MyClass { private:

T x; // data element of type T

...

public:

...

};

Here, the T acts as a “type variable.” Now we can declare objects to be of type

MyClass<int> or even MyClass< complex<double> >. (Note the extra space; we need to avoid typing << or >>.)

Templates must be placed in their entirety in the .h file and all their methods defined inline.

It is possible to define a template with more than one argument, like this:

template <class U, class V> class MyClass {

private: U x; V y;

...

};

C.5.9 Inheritance

A key feature of object-oriented programming is the ability to add features to an existing class to make a new class. For example, we may have a class Graph to represent simple graphs (no loops or multiple edges). In some cases, we may

408

C++ for Mathematicians

wish to consider graphs embedded in the plane (with adjacent vertices joined by line segments), which would be an object of type EmbeddedGraph.

Because there are many instances in which we work with abstract graphs (without embedding) we first create the Graph class. Once this is in place, we build the EmbeddedGraph class. Rather than start from scratch, we extend the Graph class like this:

class EmbeddedGraph : public Graph { private:

//additional data members to hold the x,y coordinates

//of the vertices

public:

...

};

It is useful for the constructors for EmbeddedGraph to use the constructors of Graph as first step. For example, suppose we have a constructor Graph(int n) that creates a graph with n vertices. We would want EmbeddedGraph(int n) to create the graph, but also set up a default embedding. To do this, we declare the new constructor like this:

class EmbeddedGraph: public Graph {

...

public:

EmbeddedGraph(int n) : Graph(n) { // set up the embedding

}

};

Data in Graph that are declared in the private section are not available to the added methods of EmbeddedGraph. If we want to grant access to EmbeddedGraph to these data elements, we move them from private to protected.

class Graph { private:

//data and methods that EmbeddedGraph never needs to access protected:

//data and methods that EmbeddedGraph may access

public:

// public methods, accessible to all parts of the program

};

C.6 Standard functions

C.6.1 Mathematical functions

The following mathematical functions are available in C++. Many of these are declared in the header cmath and you may need a #include <cmath> declaration to use them.

C++ Reference

409

abs: absolute value of an int. The absolute value function comes in the following flavors.

int abs(int x)

long labs(long x)

long long llabs(long long x)

double fabs(double x)

float fabsf(float x)

long double fabsl(long double x)

Of these, abs and fabs are the forms most commonly used.

acos: arc cosine. Usage: double acos(double x). Of course, this requires −1 ≤ x ≤ 1.

asin: arc sine. Usage: double asin(double x). Of course, this requires

−1 ≤ x ≤ 1.

atan and atan2: arc tangent.

The first is double atan(double x) and gives the arc tangent of x in the interval (−π/2,π/2).

The second is double atan(double x, double y) and gives the angle of the vector from the origin to the point (x,y); the result is between −π and

π.

Bessel functions. The following are available.

double j0(double x): first kind, order 0.

double j1(double x): first kind, order 1.

double jn(int n, double x): first kind, order n.

double y0(double x): second kind, order 0.

double y1(double x): second kind, order 1.

double yn(int n, double x): second kind, order n.

There are also the variants j0f through ynf that replace double with float as well as j0l through ynl that use long doubles.

cbrt: cube root. Usage: double cbrt(double x). Returns 3 x. Might not be available on all systems.

ceil: ceiling function. Usage: double ceil(double x). This returns dxe. There are also the following variants.

float ceilf(float x)

410

C++ for Mathematicians

long double ceill(long double x)

cos: cosine. Usage: double cos(double x). Gives cosx.

cosh: hyperbolic cosine. Usage: double cosh(double x). Gives coshx.

erf: error function. Usage: double erf(double x). Gives

2

Z0

x 2

erfx =

 

e−t dt.

π

There is the related function double erfc(double x) that gives 1 −erfx.

exp: ex. Usage: double exp(double x).

There is a related function double expm1(double x) that returns ex −1. See also pow.

floor: floor function. Usage: double floor(double x). This returns bxc.

There are also the following variants.

float floorf(float x)

long double floorl(long double x)

fmod: mod for reals. Usage: double fmod(double x, double y). This returns x −ny where n = bx/yc.

Gamma function. The function gamma is implemented differently on different computers. In some cases it gives (x) but in others it gives log (x).

The related gammaf and gammal work with float and long double values, respectively.

Free of ambiguity, lgamma gives log (x) and tgamma gives (x).

Some systems have additional variations of lgamma. On a UNIX system, type man gamma or man lgamma for more information.

hypot: hypotenuse. Usage: double hypot(double x, double y). Re-

p

turns x2 + y2.

log: natural logarithm. Usage: double log(double x). Returns loge x. The related double log1p(double x) that returns log(x + 1).

Also double log10(double x) returns log10 x.

pow: exponentiation. Usage: double pow(double x, double y). Returns xy. See also exp.

sin: sine. Usage: double sin(double x). Returns sinx.

C++ Reference

411

sinh: hyperbolic sine. Usage: double sinh(double x). Returns sinhx.

sqrt

: square root. Usage:

double sqrt(double x)

. Returns

 

x

.

 

 

 

tan: tangent. Usage: double tan(double x). Returns tanx.

tanh: hyperbolic tangent. Usage: double tanh(double x). Returns tanhx.

C.6.2 Mathematical constants

Various constants are defined2 via the <cmath> header. Here is a list. M PI

C++ name

Value

M_E

e

M_LOG2E

log2 e

M_LOG10E

log10 e

M_LN2

ln2

M_LN10

ln10

M_PI

π

M_PI_2

π/2

M_PI_4

π/4

M_1_PI

1/π

M_2_PI

2/π

M_2_SQRTPI

2/

 

 

 

π

 

 

 

 

M_SQRT2

2

 

1/

 

 

M_SQRT1_2

2

C.6.3 Character procedures

The following procedures require #include <cctype> and provide convenient tools for manipulating characters. Many of these are defined in terms of the int type and not the char type. This is not an issue with a function such as islower. This procedure checks if its argument represents a lowercase letter (from a to z). It would be logical to expect that the argument to islower is a char and its return value is a bool. However, both values are type int. This is inconsequential for this particular function because code such as this works as expected:

char ch;

...

if (islower(ch)) {

...

}

2Not all compilers define these constants. If your compiler does not, you can define them yourself in a header file named, say, myconstants.h. Use statements such as this: const double M PI = 3.14159...;.

412

C++ for Mathematicians

Problems arise when using a procedure such as tolower that converts uppercase characters A to Z to their lowercase form. It would be logical to expect this procedure’s argument and return types to be char, but this is not the case. Instead, both are type int. The consequence is that the statement cout<<tolower(’G’); does not print the character g on the screen. Instead, it prints the value 103. Why?

When the procedure tolower(’G’) is encountered the char value ’G’ is automatically converted into an int value (because that’s what tolower expects). Effectively, this becomes tolower(71) (because G is represented inside the computer as the number 71). Now tolower does its work and converts the code 71 for G to the code for g and returns that value: 103. That is the value that is sent (via the << operator) to cout.

The issue becomes: how do we convert the value 103 to the desired character g? Simply wrap char around the return value from tolower. The successful way to convert G to g is to use this: char(tolower(’G’)).

One additional warning: Do not use tolower("G"). The "G" is a character array (type char*) and not a single character (type char). The C++ compiler is able to convert a char* to an int, so such a statement might generate only a warning from the compiler. When run, this code would act on the memory address of the character array, and not on the character G as you intended.

Here are the procedures. All of them require #include <cctype>.

isalnum(ch): checks if ch is either a letter (upperor lowercase) or a digit (0 through 9).

isalpha(ch): checks if ch is a letter (upperor lowercase).

isdigit(ch): checks if ch is a digit (0 through 9).

isgraph(ch): checks if ch is a printable character (such as letters, digits, or punctuation) but not white space (such as a space character, a tab, or a newline).

islower(ch): checks if ch is a lowercase letter (a to z).

isprint(ch): checks if ch is a printable character (including letters, digits, punctuation, and space). A nonprintable character is known as a control character. Those can be detected with iscntrl.

ispunct(ch): checks if ch is a punctuation character.

isspace(ch): checks if ch is a white space character (such as a space, tab, or newline).

isupper(ch): checks if ch is an uppercase letter (A to Z).

isxdigit(ch): checks if ch is a digit from a hexadecimal number (i.e., one of 0 through 9, a through f, or A through F).

C++ Reference

413

tolower(ch): converts ch to a lowercase letter (if ch is an uppercase letter). Otherwise, this returns the value ch unchanged.

In most cases, use char(lower(ch)).

toupper(ch): converts ch to an uppercase letter (if ch is a lowercase letter). Otherwise, this returns the value ch unchanged.

In most cases, use char(toupper(ch)).

C.6.4 Other useful functions

The header <cstdlib> contains other functions useful for C++ programming. Here we list the ones most useful for mathematical work. (The inclusion of the header <cstdlib> might not be required on your system.)

abort: quickly halt the execution of the program. The syntax is abort(). This is an “emergency stop” procedure that brings a program to a screeching halt. A message, such as Abort trap is written to the screen.

There are better ways to stop your program. If possible, arrange your code so the program always manages to find its way to the end of the main() procedure (or to a return statement in the main). Alternatively, use exit (described below).

atof, atoi, atol: convert character arrays to numbers. The syntax for these are:

float atof(char* str)

int atoi(char* str)

long atol(char* str)

In all cases, str is a character array (not a C++ string) and the procedure converts the character array into a number. For example, atoi("23") returns an int value equal to 23.

Some platforms support an atoll procedure that converts its character array to a long long.

exit: terminate the program. Usage: exit(int x). The preferred method for ending a program is for the execution to reach a return statement in the main. Sometimes, this is not practical. In such cases, it is convenient to call exit to end the program.

The argument to exit is sent as a “return code” back to the operating system. (The return value in main serves the same purpose.) By convention, use a return value of 0 if all is well and a nonzero value if something amiss occurred (e.g., your program was unable to open a file it needed).

414

C++ for Mathematicians

rand: return pseudo random values. Usage: int rand(). This returns a “random” integer value between 0 and RAND_MAX (inclusive). See Chapter 4.

srand: initialize the random number generator. Usage: void seed(int x). This resets the pseudo random number generator to a given starting position. A good choice for a seed value is the system time. To do this, use the statement srand(time(0)); (you may need to include the ctime header).

Appendix D

Answers

This appendix provides answers and useful comments for nearly all of the exercises in this book.

1.1The first */ after the word and ends the comment. The additional */ on the last line causes the error.

1.2The first message complains that the cout object is deemed undeclared despite the fact that the programmer did not forget #include <iostream>. What the programmer did forget is the statement using namespace std; without which cout is not understood.

The second error caused a parse error on line 5. However, there is nothing wrong with line 5. The error is on line 4 where we forgot to include the semicolon. However, the compiler did not get confused by this omission until it reached line 5.

1.3No. If // or /* appear inside quotation marks, they are part of the character sequence and are not interpreted as the start of a comment.

1.4The sequence \n starts a new line. The statements cout << "\n"; and cout << endl; have the same effect.

The sequence \t causes the output to skip to the next tab stop in the output. It is useful for lining up data in columns.

The sequence \\ causes a single backslash \ to be written.

1.5Don’t make such a fuss fuss

This is more fun than learning C++!

2.1The number 3 is printed to the screen. When the float 3.5 is assigned to an int variable, there is a loss of precision; the int variable holds just the value 3. Then, when the int value e is assigned to the double variable, the double is given the value 3.

A good compiler should warn that assigning a float from an int can result in loss of precision.

2.2 The output from the program looks like this:

415