Introduction to C++ and Object- Oriented Programming for Scientific Computing Shermane Austin,

61
Introduction to C++ and Object-Oriented Programming for Scientific Computing Shermane Austin,
  • date post

    19-Dec-2015
  • Category

    Documents

  • view

    219
  • download

    1

Transcript of Introduction to C++ and Object- Oriented Programming for Scientific Computing Shermane Austin,

Introduction to C++ and Object-Oriented Programming for

Scientific ComputingShermane Austin,

Programming Exercise

• Compute sin(x) using Taylor series

• Note: convert degrees to radians

radians = degrees * pi/180

• How many terms are needed? How might the question be answered?

• Implement as a function, mysin(x) and compute sine for –pi to + pi

Boolean Data Type• Type used to generate true/false evaluation• Data type: bool• Examples: bool done = false; bool errordetected = false; while (!done && !errordetected) { }

int a, b; … bool b1 = a==b;

bool greater (int a, int b) {return a > b;}

Bool conversions

• Bools are converted to ints in logical and arithmetic operations: bool a=true; bool b = true; bool c = a + b; //a+b = 2, and is non-zero , c=true

When a result is converted back to bool type,Non-zero value = true and zero = false

File I/O

• using the fstream library

• File read: ifstream

• File output: ofstream

File Input Example

#include <fstream>#include <string>using namespace std;int main(){ ifstream fin(“myfile.txt”); int ValueCount = 0; float Sum = 0, Value; while (fin >> Value) { Sum += Value; ++ValueCount; }

File1.cc//File I/O Example 1 - reading file input and writing file output#include <fstream>#include <iostream>#include <string>using namespace std;

int main() {ifstream fin("mydata.txt");

int count = 0; float sum=0, value; while(fin >> value) {

sum += value;++count;

} if(count > 0) {

ofstream fout("output.txt"); float average = sum / count; fout << "Average: " << average << endl; } else {

cerr << "No list to average" << endl;}return 0;

}

//File I/O Example 2 - prompt user for filename#include <fstream>#include <iostream>#include <string>using namespace std;

int main() { string FileName; cout << "Enter name of file to process: "; cin >> FileName; //fstream lib uses member of string, c_str() to send as argument ifstream fin(FileName.c_str()); int count = 0; float sum=0, value; while(fin >> value) {

sum += value;++count;

} if(count > 0) {

ofstream fout("output.txt"); float average = sum / count; fout << "Average: " << average <<endl; } else { cerr << "No list to average" << endl; }

return 0;}

File I/O - checks

if (! fin) {

cerr << "Cannot open " << FileName << endl;

exit(1); }

Standard Input, Output, Error

• Standard Input – keyboard– Use cin– May be redirected

• Standard Output – terminal– Use cout– May be redirected

• Standard Error – terminal– Use cerr

Programming Exercise

Data transformationStore the following xy-data in a file0.1 1.10.2 1.80.3 2.20.4 1.8

Create a simple function to transform the y data (your choice)

Write a C++ program to read the file, transform the y data using your function and write the new xy data to a new file.

Scope

• Statement block – statements in {}

• Statement blocks are legal anywhere a statement can be placed

• Objects are used as per scope

Global Scope

• Example:

• int i

• int main()

• ...

• }

• void f()

• i++

• char i

Function parameters

• Default: pass by value

• & - using to reference

• constant parameter – function cannot change the object, e.g.

• void myfunction (const int a, int b, int c)

• b = a + 3; //legal

• a = c + 5; //illegal assignment

Examining function arguments

• Modify the simple routine to compute the area of a circle by changing the value of the radius in the function.

• In your main program, print out the value of the radius after calling the function.

• What happens?

More on functions

-void as return type signifies subroutines similar to Fortran

-return values are typed:double f(double x) { return sin(x)*pow(x,3.2); }

-Default transfer of arguments: “call by value”, e.g. x1 = 3.2; q = f(x1); f takes a copy x of x1 and the value of x1 in the callingblock (main or another function) cannot be changed.

Call by reference-Changing values of arguments requires call by reference

-C uses pointers, int n=8; … somefunc(&n); /* &n is a pointer to n */ … void somefunc(int *i) { *i = 10; // n is changed to 10 }

-Pointers also work in C++ but more commonly, references are used, e.g. int n=8; somefunc(n); void somefunc(int& i) // reference to i { i = 10; // n is changed to 10 }

Reference UseAlways uses references for large objectsThis function implies a copy of x: void somefunc(MyArray<double> x) //example of a template { ... }Copying is inefficient if the object is largeInstead use a reference in the function: void somefunc(MyArray<double>& x) { // danger: elements in the array can be changed x(5) = 10; }Manipulation of the array can be avoided using the const keyword: void somefunc(const MyArray<double>& x) { // can NOT manipulate the entries in x x(5) = 10; // illegal r = x(1); // ok }

Function Overloading

• Functions with the same name that behave differently (parameters are used to determine which function to use)

• E.g.

• void Swap( int &x, int &y)

• void Swap(float &x, float &y)

• STL – standard template library – includes functions

Memory Allocation - C++

• Dynamic memory allocation – conserves memory, e.g.

• int myarray[maxsize] is a static memory allocation, for duration of program.

• C uses malloc and free – requires size of data type and use of pointers

• C++ handles this very simply with functions new() and delete()

Dynamic memory allocation in C++

-C++ new and delete

double* x = new double[n]; //creates an array of n elements// delete [] x; // deallocates memory for the array// allocate a single variable:double* p = new double;delete p;double* x = new double[n];.

Object-Oriented Programming Paradigm

• Encapsulation– Objects are analogous to data structures– Data hiding or protection– Only member functions can access data

• Inheritance– Derive new objects from existing objects– SparseMatrix from Matrix object

• Polymorphism

C++ Classes

A class is a collection of data structures and operations on them

An object is an instance of a class

The MatDense object is a good example:data: matrix size + array entriesoperations: creating a matrix, accessing matrix entries,matrix-vector products,..

A class is essentially a new data type

A class can contain other objects. Complex types can be created that are more easily manipulated.

Class construct

• Class – programmer-defined data type

• Terminology– Data or attributes

– Member functions

• Define:– attributes or data– access: public, private (also protected and friend)

Generic Types of Member Functions

• Constructors – create new objects

• Inspectors – view attributes of objects

• Mutators – change attributes of objects

• Facilitators – operations on objects

• Destructors – destroy objects

Class Rational

Public Interface: Add(), Subtract(), Multiply(), Divide(), Equal(), LessThan(), Insert(), Extract()Data Members:

NumeratorValue, DenominatorValueOther members: Numerator(), Denominator(), SetNumerator(), SetDenominator()

Client Program Example

#include <iostream>#include <string>#include “rational.h”

using namespace std;

int main() { Rational r(3/4), s(2/3); Rational t(r);

Rational Sum = r + s; Rational Product = r * s;

cout << r << “+” << s << “=“ << Sum << endl;

return(0);}

Rational Class Interface Description

//rational.h

#include <iostream>#include <string>

Using namespace std;

//Class descriptionClass Rational {

public: // member functions //constructors

Rational(); Rational( int numer, int denom=1); //some arithmetic and stream facilitators …

Example: Rational Class• Want to represent rational numbers• Think of as a new data structure• Member data – numerator and denominator• Member functions would include:

– Constructors– Arithmetic functions

a/b + c/d = (ad+bc)/bda/b – c/d = (ad-bc)/bda/b * c/d = ac/bda/b / c/d = ad/bc

– Copy – Compare– Display– Extract value– Inspect values– Set values

• auxilary functions and operators

• operator overloading

An example

Initialize A and x in a single function: void init (MyArray<double>& A, MyList<double>& x) { const int n = x.size(); int i,j; for (j=1; j<=n; j++) { x(j) = j/2.0; // or double(j)/2.0 for (i=1; i<=n; i++) { A(i,j) = 2.0 + double(i)/double(j); } } }

Complex Arithmetic

Develop a class for complex numbers

Note: C++ already has a class complex in its standard templatelibrary (STL), use that instead of the complex example

#include <complex>using namespace std;…complex<double> z(5.3,2.1);complex<double> y(0.3);cout << z*y + 3;

Using the new Complex Class

#include “complex.h"void main (){ Complex a(0,1); // imaginary unit Complex b(2), c(3,-1); Complex q = b; cout << "q=" << q << ", a=" << a << ", b=" << b << "\n"; q = a*c + b/a; cout << "Re(q)=" << q.Re() << ", Im(q)=" << q.Im() << "\n";}

Basic contents of Complex class

Data members: real and imaginary part (of the complex number)Functions: construct or initialize complex numbersComplex a(0,1); // imaginary unitComplex b(2), c(3,-1);

Write out complex numbers:cout << "a=" << a << ", b=" << b << "\n";

Perform arithmetic operations:q = a*c + b/a;

Declaration of Complex Class

class Complex{private: // this is the default double re, im; // real and imaginary partpublic:Complex (); // Complex c;Complex (double re, double im = 0.0); // Complex a(4,3);Complex (const Complex& c); // Complex q(a);~Complex () {}Complex& operator= (const Complex& c); // a = b;double Re () const; // double real_part = a.Re();double Im () const; // double imag_part = a.Im();double abs () const; // double m = a.abs(); // modulusfriend Complex operator+ (const Complex& a, const Complex& b);friend Complex operator- (const Complex& a, const Complex& b);friend Complex operator* (const Complex& a, const Complex& b);friend Complex operator/ (const Complex& a, const Complex& b);}; //friend means that stand-alone functions can work on private parts(re, im)

Simple member functions

Extract the real and imaginary part (recall: these are private, i.e.,invisible for users of the class; here we get a copy of them forreading)

double Complex:: Re () const { return re; }double Complex:: Im () const { return im; }

Computing the modulus:double Complex:: abs () const { return sqrt(re*re + im*im); }

Using constconst variables cannot be changed: const double p = 3; p = 4; // ILLEGAL!! compiler error...

const arguments (in functions) cannot be changed: double myabs (const Complex& c) { return sqrt(c.re*c.re + c.im*c.im); }c cannot be changed, e.g. c.re=0 is illegal

const Complex arguments can only call const memberfunctions:double myabs (const Complex& c){ return c.abs(); } // ok because c.abs() is a const function

Without const, i.e.,double Complex:: abs () { return sqrt(re*re + im*im); }the compiler would not allow the c.abs() call in myabs

Overloaded operators

C++ allows us to define + - * / for arbitrary objectsThe meaning of + for Complex objects is defined in the functionComplex operator+ (const Complex& a, const Complex& b); // a+bThe compiler translatesc = a + b;intoc = operator+ (a, b);i.e., the overhead of a function callIt would be more efficient to have the function body inlined, i.e.,copied into the calling code

This is enabled by the inline keyword:inline Complex operator+ (const Complex& a, const Complex& b){ return Complex (a.re + b.re, a.im + b.im); }

Using inlineIf operator+, operator= and the constructorComplex(r,i) all are inline functions,c = a + b;that is,c.operator= (operator+ (a,b));is transformed toc.re = a.re + b.re;c.im = a.im + b.im;by the compiler, i.e., no function calls

This is particularly crucial for loops, e.g.,Complex s; // have also Complex a and bfor (i = 1; i <= huge_n; i++) {s = s + a;a = a/3.0;}Without inlining s=s+a we introduce two function calls inside a loop,which prevent aggressive optimization by the compiler

Constructors

Constructors have the same name as the classThe declaration statementComplex q;calls the member function Complex()

A possible implementation isComplex:: Complex () { re = im = 0.0; }meaning that declaring a complex number means making thenumber (0,0)Alternative:Complex:: Complex () {}Downside: no initialization of re and im

Constructors with arguments

The declaration statementComplex q(-3, 1.4);calls the member function Complex(double, double)A possible implementation isComplex:: Complex (double re_, double im_){ re = re_; im = im_; }

The Assignment operator

a = b implies a call to a.operator= (b)– this is the definition of assignmentImplement operator= as a part of the class:Complex& Complex:: operator= (const Complex& c){re = c.re;im = c.im;return *this;}If you forget to implement operator=, C++ will make one (justmemberwise copy – dangerous if you perform dynamic memorymanagement!)

Copy constructor

The statementsComplex q = b;Complex q(b);makes a new object q, which becomes a copy of bSimple implementation in terms of the assignment:Complex:: Complex (const Complex& c){ *this = c; }this is a pointer to “this object”, *this is the present object, so*this = c means setting the present object equal to c, i.e.,this->operator= (c)

Output functionOutput format of a complex number: (re,im), i.e., (1.4,-1)

Desired user syntax:std::cout << c;

any_ostream_object << c;The effect of << for a Complex object is defined in

ostream& operator<< (ostream& o, const Complex& c){ o << "(" << c.Re() << "," << c.Im() << ") "; return o;}

The input operator (operator>>) is more complicated (need torecognize parenthesis, comma, real numbers)

Header fileWe divide the code of class Complex into a header file Complex.h

(extension .h) and a file Complex.cpp with the body of thefunctions (extension .cpp)

Example: class MyVectorClass MyVector: a vector

Data: plain C arrayFunctions: subscripting, change length, assignment to another

vector, inner product with another vector, ...This examples demonstrates many aspects of C++ programming

Note: this is mainly an educational example; for professional use oneshould use a ready-made vector class (std::valarray for

instance)

MyVector functionality ICreate vectors of a specified length:

MyVector v(n);Create a vector with zero length:

MyVector v;Redimension a vector to length n:

v.redim(n);Create a vector as a copy of another vector w:

MyVector v(w);Extract the length of the vector:

const int n = v.size();

MyVector functionality IIExtract an entry:double e = v(i);

Assign a number to an entry:v(j) = e;

Set two vectors equal to each other:w = v;

Take the inner product of two vectors:double a = w.inner(v);

or alternativelya = inner(w,v);

MyVector functionality IIIWrite a vector to the screen:

v.print(std::cout);Arithmetic operations with vectors:

// MyVector u, y, x; double au = a*x + y; // ’DAXPY’ operation

The proposed syntax is defined through functions in classMyVector

Class MyVector holds both the data in the vector, the length of thevector, as well as a set of functions for operating on the vector data

Users can only operate on the vector data through the offeredfunctions

MyVector objects can be sent to Fortran and C functions asfollows:

// v is MyVectorcall_my_F77_function (v.getPtr(), v.size(), ...)

// array length

The MyVector classclass MyVector

{private:

double* A; // vector entries (C-array)int length;

void allocate (int n); // allocate memory, length=nvoid deallocate(); // free memory

public:MyVector (); // MyVector v;

MyVector (int n); // MyVector v(n);MyVector (const MyVector& w); // MyVector v(w);

~MyVector (); // clean up dynamic memorybool redim (int n); // v.redim(m);

MyVector& operator= (const MyVector& w);// v = w;double operator() (int i) const; // a = v(i);

double& operator() (int i); // v(i) = a;void print (std::ostream& o) const; // v.print(cout);

double inner (const MyVector& w) const; // a = v.inner(w);int size () const { return length; } // n = v.size();

double* getPtr () { return A; } // send v.getPtr() to C/F77};

// operators:MyVector operator* (double a, const MyVector& v); // u = a*v;MyVector operator* (const MyVector& v, double a); // u = v*a;

MyVector operator+ (const MyVector& a, const MyVector& b); // u = a+b;

Constructors IConstructors tell how we declare a variable of type MyVector and

how this variable is initializedMyVector v; // declare a vector of length 0// this actually means calling the function

MyVector::MyVector (){ A = NULL; length = 0; }

Constructors IIMyVector v(n); // declare a vector of length n

// means calling the functionMyVector::MyVector (int n)

{ allocate(n); }void MyVector::allocate (int n)

{length = n;

A = new double[n]; // create n doubles in memory

DestructorA MyVector object is created (dynamically) at run time, but must

also be destroyed when it is no longer in use. The destructorspecifies how to destroy the object:

MyVector::~MyVector (){

deallocate();}

// free dynamic memory:void MyVector::deallocate ()

{delete [] A;

}

The assignment operatorSet a vector equal to another vector:

// v and w are MyVector objectsv = w;

means callingMyVector& MyVector::operator= (const MyVector& w)

// for setting v = w;{

redim (w.size()); // make v as long as wint i;

for (i = 0; i < length; i++) { // (C arrays start at 0)A[i] = w.A[i];

}return *this;

}// return of *this, i.e. a MyVector&, allows nested

// assignments:u = v = u_vec = v_vec;

Redimensioning the lengthChange the length of an already allocated MyVector object:

v.redim(n); // make a v of length nImplementation:

bool MyVector::redim (int n){

if (length == n)return false; // no need to allocate anything

else {if (A != NULL) {

// "this" object has already allocated memorydeallocate();

}allocate(n);

return true; // the length was changed}}

The copy constructorCreate a new vector as a copy of an existing one:

MyVector v(w); // take a copy of wMyVector::MyVector (const MyVector& w)

{allocate (w.size()); // "this" object gets w’s length

*this = w; // call operator=}

this is a pointer to the current (“this”) object, *this is the objectitself

To the point: subscripting// a and v are MyVector objects; want to set

a(j) = v(i+1);// the meaning of a(j) is defined by

inline double& MyVector::operator() (int i){

return A[i-1];// base index is 1 (not 0 as in C/C++)

}Inline functions: function body is copied to calling code, no overhead

of function call!Note: inline is just a hint to the compiler; there is no guarantee that

the compiler really inlines the functionWhy return a double reference?

double& MyVector::operator() (int i) { return A[i-1]; }// returns a reference (‘‘pointer’’) directly to A[i-1]

// such that the calling code can change A[i-1]

Inlining// given MyVector a(n), b(n), c(n);

for (int i = 1; i <= n; i++)c(i) = a(i)*b(i);

// compiler inlining translates this to:for (int i = 1; i <= n; i++)

c.A[i-1] = a.A[i-1]*b.A[i-1];// or perhaps

for (int i = 0; i < n; i++)c.A[i] = a.A[i]*b.A[i];

// more optimizations by a smart compiler:double* ap = &a.A[0]; // start of adouble* bp = &b.A[0]; // start of bdouble* cp = &c.A[0]; // start of c

for (int i = 0; i < n; i++)cp[i] = ap[i]*bp[i]; // pure C!

Inlining and the programmer’s complete control with the definition ofsubscripting allow

safe indexingefficiency as in C or Fortran

TemplatesTemplates are the native C++ constructs for parameterizing parts of

classesMyVector.h:

template<typename Type>class MyVector

{Type* A;int length;

public:...

Type& operator() (int i) { return A[i-1]; }...};

Declarations in user code:MyVector<double> a(10);MyVector<int> counters;

Much simpler to use than macros for parameterization, but portabilityproblems still exist

Exercise: Get started with classesMake a small program with the following code:

class X{

private:int i,j;public:

X(int i, int j);void print() const;

};X::X(int i_, int j_){ i = i_; j = j_; }

void X::print() const{

std::cout << "i=" << i << " j=" << j << ’\n’;}

plus a main program testing class X:X x(3,9); x.print();

A vector class – p. 186Exercise continued

Compile and runHow can you change the class such that the following code is legal:

X myx; myx.i=5; myx.j=10; myx.print();