Because such a procedure does not modify the set, we declare it such as this: long sumconst set& S { and in the program file, sayLineSegment.cc, we find this: double LineSegment::length
Trang 1C.1.5 Constants and the keywordconst
If a variable is declared with a given value and the program never changes thatvalue, then the variable should be declaredconst For example, in a program thatuses the Golden Mean, we would have this:const phi = (1.+sqrt(5.))/2.;.Arguments to procedures that are not modified by the procedure should be de-claredconst For example, suppose we create a procedure to calculate the sum ofthe elements in a set of integers Because such a procedure does not modify the set,
we declare it such as this:
long sum(const set<long>& S) {
and in the program file, sayLineSegment.cc, we find this:
double LineSegment::length() const {
// calculate and return the length
Trang 2C.2 Operations
Here we describe the behavior of C++’s fundamental operations Remember, ever, that classes can override these meanings (e.g., the<<operator does input whenused withcinbut its fundamental use is to shift bits)
how-C.2.1 Assignment
The statementa=b;takes the value held in the variableband places that value inthe variablea Assignment statements can be combined The statementa=b=c;isequivalent toa=(b=c);and the effect is to take the value incand store that value
in bothbanda
C.2.2 Arithmetic
The basic arithmetic operators are+, -, *, and/ For integer types, the modoperator is% There is no exponentiation operator, but thepowandexpproceduresfill this gap
The- operator can be used as a unary operator; this negates its argument Forexample, ifaholds the value 3, thenb=-a; assigns tobthe value −3 (leavinga
Trang 3state-The keywordsizeofis an operator that returns the number of bytes required tohold a type; for example,sizeof(double).
The comma is used for separating procedure arguments and when declaring morethan one variable of a given type (e.g.,double x,y;) It also has the obscure pur-pose of combining two (or more) expressions into a single statement For example,
in aforloop, if we want to initialize two counters, we may have a code such as this:
int a, b;
for (a=N,b=0; a-b>0; a ,b++) {
// stuff to do
}
This initializesawith the value held inNandbwith 0
The following operators are for working with pointers
Ampersand is the address-of operator Ifxis anintvariable, then&xreturns apointer toxof typeint* The ampersand is also used to specify call-by-referencesemantics in procedures
Asterisk is the pointer dereferencing operator Ifpis a pointer to an integer value,then*pis the value held in the location pointed to byp
The arrow operator, ->, combines the action of.and* For example, ifpis apointer to an object, thenp->xis equivalent to(*p).x(the data elementxof theobjectppoints to) andp->alpha()is equivalent to(*p).alpha()(invoke method
alphaon the object pointed to byp)
Trang 4C.3.2 Looping: for,while, anddo
Theforstatement has the following format,
for (start_statement; test_expression; advance_statement) {
work_statements;
}
This statement results in the following actions First, thestart_statementis cuted Thentest_expressionis evaluated; iffalsethen the loop exits and con-trol passes to the statement following the close brace Otherwise (test_expression
exe-istrue), the work_statementsare executed, then theadvance_statementisexecuted, and finally thetest_expressionis evaluated Iffalse, the loop exitsand iftrue, the entire process repeats
Trang 5Thewhilestatement is structured as follows,
while (test_expression) {
work_statements;
}
The test_expressionis evaluated first and, iffalse, thework_statements
are skipped and execution passes to the next statement after the close brace erwise (test_expression istrue), the work_statements are executed, then
Oth-test_expressionis re-evaluated and the process repeats
Thedostatement is structured as follows,
If no matching label can be found, then statements following thedefaultlabel areexecuted Groups of statements may be preceded with more than one label
The labelsval1,val2, and so on, must be specific numbers (not variables)
Trang 6C.3.4 goto
C++ contains agotostatement whose syntax isgoto label; The causes cution to pass to a statement that has been flagged with the namelabel In general,the use ofgotois discouraged as it can lead to unintelligible programs However,one use is for breaking out of a double loop:
exe-for (int a=0; a<N; a++) {
Ifsomething_badisfalse, execution continues withmore_statementsand the
recovery_statements are skipped However, ifsomething_badevaluates to
true, thenmore_statementsare skipped and the valuexis “thrown” ing thatxis of typetype, the exception is “caught” by thecatchstatement and
Assum-recovery_statementsare executed
The statements inside thetryblock need not have an explicitthrowstatement;the procedures invoked inside this block may throw exceptions See Section 15.3
C.4 Procedures
Procedures (often called functions in the programming community) are grams designed to do a particular job
subpro-Procedures are declared by specifying a return type (if none, writevoid), followed
by the procedure’s name, followed by a list of arguments (with their types) The valuereturned by the procedure is given by areturnstatement
Two procedures may have the same name provided they have different numberand/or types of arguments
Trang 7C.4.1 File organization
In general, it is best to separate the declaration of a procedure from its definition.The declaration is placed in a header file (suffix.h) and the full definition is placed
in a code file (suffix.cc)
For example, suppose we wish to declare a procedure namednrootsthat returnsthe number of real roots of a quadratic polynomial ax2+ bx + c The header filewould contain the following single line,
int nroots(double a, double b, double c);
The.ccfile would contain the full specification:
int nroots(double a, double b, double c) {
C.4.2 Call by value versus call by reference
By default, C++ procedures use call-by-value semantics That is, when a dure (such asnroots) is invoked, the values of the arguments in the calling proce-dure are copied to the local variables in the procedure Although procedures maymodify the copies of the arguments, the original values (in the parent procedure) areunaffected
proce-However, variables can be designated to use call-by-reference semantics This isindicated by inserting an ampersand between the type and the argument In this case,
a procedure can change a value from its calling procedure
Trang 8C.4.3 Array (and pointer) arguments
When an array is passed to a procedure, C++ does not make a copy of the array;instead it sends a pointer to the first element of the array
For example, suppose we write a procedure to sum the elements in an array of
doublevalues Here is the code
double sum(const double* array, long nels) {
example we presented here does not, in fact, modify the elements of the array, wecertify that with the keywordconst
More generally, a pointer can be passed to a procedure In this case, the valuepointed to by the pointer can be modified by the procedure However, it is simpler touse reference arguments
C.4.4 Default values for arguments
Arguments to procedures may be given default values If an argument is given adefault value, then all arguments to its right must also be given default values Forexample:
void example(int a, double x = 3.5, int n = 0) {
Trang 9commer-C.5.1 Overview and file organization
Suppose we wish to create a class named MyClass The specification for theclass is broken across two files: MyClass.hcontains a declaration of the class and
MyClass.cccontains code for the class’s methods
The code inMyClass.htypically looks like this:
int MyClass::method1(double x, double y) {
// code for this method
}
Alternatively, code for constructors and methods may be given inline in the.h
file This is advisable when the code is only a few lines long
Trang 10Data and methods listed in theprivatesection are accessible only to the methodsinside the class (but see Appendix C.5.7) Data and methods listed in thepublic
section are accessible to all parts of the program
It is wise to designate all data in a class private and to provide get/set methods toinspect/manipulate the data
C.5.2 Constructors and destructors
A constructor is a class method that is invoked when an object of the class iscreated (e.g., when declared) Constructors do not have a return type, but may havearguments
Be sure to have a zero-argument constructor for your class This constructor isinvoked when a variable is declared in the simple form:MyClass X; The objectX
is then initialized using the zero-argument constructor
Classes may have constructors with arguments For example, ifMyClasshas aconstructor with a single integer argument, then the declarationMyClass X(17);
invokes that constructor Such a constructor is also invoked in all the followingsituations
Trang 11C.5.3 Operators
The usual C++ operators (such as+and*) apply to the built-in data types (int,
double, and so on) These operators may also be used for classes by creating ator methods and procedures
oper-Suppose we wish to define+for objects of typeMyClass; that is, ifAandBaretypeMyClass, then we want to ascribe a meaning to A+B Typically, we woulddeclare a method within the body of theMyClassdeclaration (in the hfile) likethis:
Then, when the compiler encounters the expressionA+B, it applies theoperator+
method for objectAwith objectBpassed (by reference) toZ
(Note the double appearance of the keywordconst The firstconstcertifies thatthis method does not modify the argumentZand the second certifies that this methoddoes not modify the object on which it is invoked.)
Alternatively, we could implementA+Bwith a procedure that is not a class method
In a.hfile we declare the procedure like this:
MyClass operator+(const MyClass& U, const MyClass& V);
and in the corresponding.ccfile we give the code:
MyClass operator+(const MyClass& U, const MyClass& V) {
or as procedures like this:
MyClass operator-(const MyClass& U);
Binary operators may be used to combine objects of different types If the leftoperand of the operator is of typeMyClass, then the operator may be defined as amethod ofMyClass For example, for the operatorMyClass+int, use this:
Trang 12class MyClass {
MyClass operator+(int j) const;
};
However, forint+MyClass, a procedure needs to be declared like this:
MyClass operator+(int j, const MyClass& Z);
The increment++and decrement operators have two forms: ++AandA++ Werecommend defining only the prefix form This is done with an class method likethis:
modi-It is possible to declare a postfix form of these operators To do this, we give a
“dummy” argument of typeintlike this:
class MyClass {
MyClass operator (int x);
};
C.5.4 Copy and assign
If objectsA andB are of typeMyClass, then the expressionA=B has a defaultmeaning that can (and sometimes should) be overridden
The default meaning ofA=Bis to copy the data fields ofBinto the correspondingdata fields ofA That is, if classMyClasshas data fieldsx,y, andz, thenA=B;hasthe effect of performing the three assignments
A.x = B.x; A.y = B.y; A.z = B.z;
Finally, the new value ofAis returned
This behavior is appropriate in many cases, especially if the data fields are thebasic types However, if one of these fields, say x, is an array, then the action
A.x = B.x;does not copy the arrayB.xintoA.x(as, presumably, we would want).Rather, it causesA.xto point to the same location in memory asB.x, so subsequentmodifications to array elements in B.x are also applied to the arrayA.x becausethese arrays are now housed in the same memory
To achieve the desired behavior, we need to write a new assignment operator Theeffect of this operator is to copy the data fromBtoA In addition, an assignmentoperator should return the value ofA(after it is updated)
To do this, we declare anoperator=method inside the class definition like this:
class MyClass {
Trang 13MyClass operator=(const MyClass& Z);
};
In the.ccfile, the code looks like this:
MyClass MyClass::operator(const MyClass& Z) {
// set the fields x, y, and z
// so they are duplicates of Z.x, Z.y, Z.z
To declare a copy constructor, we have the following in the.hfile,
// set the fields x, y, and z
// so they are duplicates of Z.x, Z.y,, Z.z
}
C.5.5 staticdata and methods
In a typical class, each object of the class has its own values for each data field.Sometimes it is desirable to have a value that is shared among all objects in theclass For example, if we wish to keep track of how many objects of typeMyClass
are currently in existence, we can define a variable object_countthat is sharedamong all objects of typeMyClass Constructors would increment this variable anddestructors would decrement it To distinguish variables that are shared among allobjects from ordinary data members that are particular to each instance of the class,
we use the keywordstatic For example, in the.hfile we have
Trang 14and in the.ccfile we have
MyClass::object_count = 0;
One may also havestaticmethods These are class methods that do not apply
to any particular object, but to the class as a whole They can only accessstatic
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 oftypeMyClasscurrently exist To that end, we add to thepublicsection ofMyClass
the following method,
static int get_object_count() { return object_count; }
Because this method applies to the classMyClassand not to any particular object ofthat type, it is inappropriate to invoke it asA.get_object_count()(whereAis oftypeMyClass) Rather, we writeMyClass::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 wasinvoked For example, when we define ++A (by declaring anoperator++), theconventional return value of this operator is the new value assigned to the object
To do this, C++ provides a pointer namedthis Thethispointer refers to theobject on which it is invoked SupposeMyClasshas a method namedwork If weuse the pointerthisinside ofwork, thenthisis a pointer to the objectAand*this
is the objectAitself
So, when we define operator++() for MyClass, the final statement of thatmethod would bereturn *this;
Likewise, when we define an assignment operator, the return value of that operatorshould be the new value of the left-hand argument Ending withreturn *this;
accomplishes this task
C.5.7 Friends
Private data elements of a class can be accessed by class methods, but not by otherprocedures However, it is possible to grant a procedure special access to private dataelements by declaring that procedure to be afriendof the class
To do this, we need to declare thefriendprocedure inside the class declarationlike this:
Trang 15It is generally safer not to allow any functions to have access to the data elements
of a class The use offriendprocedures subverts the goal of data hiding In otherwords: Don’t use this feature of C++
Furthermore, it is possible to have a procedure that is afriendof two differentclasses, or for one class to be afriendof another These advanced topics are beyondthe scope of this text
C.5.8 Class templates
C++ implements complex numbers using a class template For complex numberswithdoublereal and imaginary parts, we use the declarationcomplex<double>
whereas for Gaussian integers we usecomplex<int>
We create our own class templates like this:
Here, the T acts as a “type variable.” Now we can declare objects to be of type
MyClass<int>or evenMyClass< 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 methodsdefined inline
It is possible to define a template with more than one argument, like this:
template <class U, class V>
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 classGraph
to represent simple graphs (no loops or multiple edges) In some cases, we may
Trang 16wish to consider graphs embedded in the plane (with adjacent vertices joined by linesegments), which would be an object of typeEmbeddedGraph.
Because there are many instances in which we work with abstract graphs (withoutembedding) we first create the Graph class Once this is in place, we build the
EmbeddedGraphclass Rather than start from scratch, we extend theGraphclasslike this:
class EmbeddedGraph : public Graph {
It is useful for the constructors for EmbeddedGraphto use the constructors of
Graph as first step For example, suppose we have a constructorGraph(int n)
that creates a graph with n vertices We would wantEmbeddedGraph(int n) tocreate the graph, but also set up a default embedding To do this, we declare the newconstructor like this:
class EmbeddedGraph: public Graph {
Trang 17• abs: absolute value of anint The absolute value function comes in thefollowing flavors.
– long double fabsl(long double x)
Of these,absandfabsare the forms most commonly used
• acos: arc cosine Usage: double acos(double x) Of course, this quires −1 ≤ x ≤ 1
re-• asin: arc sine Usage:double asin(double x) Of course, this requires
−1 ≤ x ≤ 1
• atanandatan2: arc tangent
The first isdouble atan(double x)and gives the arc tangent of x in theinterval (−π/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 variantsj0fthroughynfthat replacedoublewithfloat
as well asj0lthroughynlthat uselong 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)
Trang 18– long double ceill(long double x)
• cos: cosine Usage:double cos(double x) Gives cos x
• cosh: hyperbolic cosine Usage:double cosh(double x) Gives cosh x
• erf: error function Usage:double erf(double x) Gives
erf x =√2
π
Z x 0
e−t2dt
There is the related functiondouble erfc(double x)that gives 1 − erf x
• exp: ex Usage:double exp(double x)
There is a related functiondouble expm1(double x)that returns ex− 1.See alsopow
• floor: floor function Usage: double floor(double x) This returnsbxc
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) Thisreturns x − ny where n = bx/yc
• Gamma function The functiongammais implemented differently on differentcomputers In some cases it gives Γ(x) but in others it gives log Γ(x)
The relatedgammafandgammalwork withfloatandlong doublevalues,respectively
Free of ambiguity,lgammagives log Γ(x) andtgammagives Γ(x)
Some systems have additional variations oflgamma On a UNIX system, type
man gammaorman lgammafor more information
• hypot: hypotenuse Usage: double hypot(double x, double y) turnspx2+ y2
Re-• log: natural logarithm Usage:double log(double x) Returns logex.The relateddouble log1p(double x)that returns log(x + 1)
Alsodouble log10(double x)returns log10x
• pow: exponentiation Usage: double pow(double x, double y) turns xy See alsoexp
Re-• sin: sine Usage:double sin(double x) Returns sin x
Trang 19• sinh: hyperbolic sine Usage:double sinh(double x) Returns sinh x.
• sqrt: square root Usage:double sqrt(double x) Returns√
x
• tan: tangent Usage:double tan(double x) Returns tan x
• tanh: hyperbolic tangent Usage:double tanh(double x) Returns tanh x
be logical to expect that the argument toisloweris acharand its return value is a
bool However, both values are typeint This is inconsequential for this particularfunction because code such as this works as expected:
2 Not 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 ;
Trang 20Problems arise when using a procedure such astolowerthat converts uppercasecharacters A to Z to their lowercase form It would be logical to expect this proce-dure’s argument and return types to bechar, but this is not the case Instead, bothare typeint The consequence is that the statementcout<<tolower(’G’);doesnotprint the charactergon the screen Instead, it prints the value 103 Why?When the proceduretolower(’G’)is encountered thecharvalue’G’is auto-matically converted into anintvalue (because that’s whattolowerexpects) Ef-fectively, this becomestolower(71)(because G is represented inside the computer
as the number 71) Nowtolowerdoes its work and converts the code 71 for G tothe code for g and returns that value: 103 That is the value that is sent (via the<<
operator) tocout
The issue becomes: how do we convert the value 103 to the desired character g?Simply wrapchararound the return value fromtolower The successful way toconvert G to g is to use this:char(tolower(’G’))
One additional warning: Do not usetolower("G") The"G"is a character array(typechar*) and not a single character (typechar) The C++ compiler is able toconvert achar*to anint, so such a statement might generate only a warning fromthe compiler When run, this code would act on the memory address of the characterarray, and not on the character G as you intended
Here are the procedures All of them require#include <cctype>
• isalnum(ch): checks ifchis either a letter (upper- or lowercase) or a digit(0 through 9)
• isalpha(ch): checks ifchis a letter (upper- or lowercase)
• isdigit(ch): checks ifchis a digit (0 through 9)
• isgraph(ch): checks ifchis a printable character (such as letters, digits,
or punctuation) but not white space (such as a space character, a tab, or anewline)
• islower(ch): checks ifchis a lowercase letter (a to z)
• isprint(ch): checks ifch is a printable character (including letters, its, punctuation, and space) A nonprintable character is known as a controlcharacter Those can be detected withiscntrl
dig-• ispunct(ch): checks ifchis a punctuation character
• isspace(ch): checks ifchis a white space character (such as a space, tab,
or newline)
• isupper(ch): checks ifchis an uppercase letter (A to Z)
• isxdigit(ch): checks ifchis a digit from a hexadecimal number (i.e., one
of 0 through 9, a through f, or A through F)
Trang 21• tolower(ch): convertschto a lowercase letter (ifchis an uppercase letter).Otherwise, this returns the valuechunchanged.
In most cases, usechar(lower(ch))
• toupper(ch): convertschto an uppercase letter (ifchis a lowercase letter).Otherwise, this returns the valuechunchanged
In most cases, usechar(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 theheader<cstdlib>might not be required on your system.)
• abort: quickly halt the execution of the program The syntax isabort().This is an “emergency stop” procedure that brings a program to a screechinghalt A message, such asAbort trapis 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 areturn statement in themain) Alternatively, useexit
anintvalue equal to 23
Some platforms support anatollprocedure that converts its character array
to along long
• exit: terminate the program Usage: exit(int x) The preferred methodfor ending a program is for the execution to reach areturnstatement in the
main Sometimes, this is not practical In such cases, it is convenient to call
exitto end the program
The argument toexitis sent as a “return code” back to the operating system.(Thereturnvalue inmainserves the same purpose.) By convention, use areturn 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)
Trang 22• rand: return pseudo random values Usage: int rand() This returns a
“random” integer value between 0 andRAND_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 thectimeheader)
Trang 23The second error caused aparse erroron line 5 However, there is ing wrong with line 5 The error is on line 4 where we forgot to include thesemicolon However, the compiler did not get confused by this omission until
noth-it reached line 5
1.3 No If//or/*appear inside quotation marks, they are part of the charactersequence and are not interpreted as the start of a comment
1.4 The sequence \n starts a new line The statementscout << "\n"; and
cout << endl;have the same effect
The sequence\tcauses 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.5 Don’t make such a fuss fuss
This is more fun than learning C++!
2.1 The number3is printed to the screen When thefloat3.5 is assigned to an
intvariable, there is a loss of precision; theintvariable holds just the value
3 Then, when theintvalue e is assigned to thedoublevariable, thedouble
is given the value 3
A good compiler should warn that assigning afloatfrom anintcan result
in loss of precision
2.2 The output from the program looks like this:
415
Trang 24Onlycout << (4./3)*10gives the proper output.
2.3 The output of the program looks like this:
The mathematician’s mod operation a mod n is usually only defined for n > 0
and a mod n is an element of {0,1,2, ,n − 1} Thus, −5 mod 3 = 1, but
C++ returns−2 for(-5)%3 In addition, C++ allows the modulus, n, to be
negative
2.4 Both’3’and3are integer types (the first is acharwhich is considered to be
an integer type) The result of this expression isfalsebecause the character
’3’does not have the same value as the integer 3 Indeed, on many ers’3’has the value 51 because the character’3’is the 51st in the ASCII
comput-character set
2.5 The first occurrence of the expressiona==bevaluates tofalsebecause, whenthis expression is reached,aandbhold different values When aboolvalue
offalseis written to the screen, the number0is used
Next, the expressiona=bhas the effect of copyingb’s value intoaand returns
the value now held in common inaandb Hence, 10 is printed
Finally, when the second occurrence ofa==bis evaluated, bothaandbholdthe value 10 and so the expression evaluates totruewhich is printed as1onthe screen
2.6 The result of your experiment might depend on the computer on which it isrun The following is the typical result
If the numerator and denominator are bothinttypes, both 00 and10 evaluate
2.8 (a) A variable name may not begin with a digit
(b) The minus sign is an operator and may not be part of a variable’s name
Trang 25(c) The worddoubleis a C++ keyword and may not be used as a variablename.
(d) A variable name may not include a colon
(e) The ampersand is an operator and may not be part of a variable’s name.(f) This begins with a digit, contains a minus sign, and is a number (equal
to 0.01).
3.1 d = 17, x = 6, and y = −1 Other answers for x and y are possible.
3.2 If this procedure is invoked withnequal to−1 we fall into an infinite
recur-sion
There is also the issue that if a large value of n is passed to this procedure,the result would be too large to fit in along, overflow would result, and theanswer returned would be incorrect
3.3 In this implementation, we take the input argument to be a real number (type
double) and the return value to be anint Your choice may vary
Trang 263.5 The iterative version is significantly faster than the recursive version When
the iterative version computes F n , it does n − 1 additions However, when the
recursive version computes F n , it requests the calculation of F n−1 and F n−2
The calculation of F n−1 also requests the computation of F n−2, and this is
wasted effort The amount of work needed to calculate F ngrows exponentially
with n.
When asked to calculate F n, the recursive procedure presented here is called
2F n − 1 times (your program may have different behavior) This is easy to
show by induction Let c(n) denote the number of timesfibonacciis called
when asked to calculate F n For n = 0,1 we have c(n) = 1 and for n > 1 we note that c(n) = 1+c(n−1)+c(n−2) Now one checks that 2F n −1 satisfies
this recurrence with the given initial conditions
The values you were requested to find are
cout << "zeta2up = " << zeta2up(n) << endl;
cout << "zeta2down = " << zeta2down(n) << endl;
cout << "truth = " << zeta2 << endl;