3 Representation in C++.

Similar documents
Determine If An Equation Represents a Function

Storage Classes CS 110B - Rule Storage Classes Page 18-1 \handouts\storclas

KITES TECHNOLOGY COURSE MODULE (C, C++, DS)

PART-A Questions. 2. How does an enumerated statement differ from a typedef statement?

An Incomplete C++ Primer. University of Wyoming MA 5310

Polymorphism. Problems with switch statement. Solution - use virtual functions (polymorphism) Polymorphism

PROBLEM SOLVING SEVENTH EDITION WALTER SAVITCH UNIVERSITY OF CALIFORNIA, SAN DIEGO CONTRIBUTOR KENRICK MOCK UNIVERSITY OF ALASKA, ANCHORAGE PEARSON

C++FA 5.1 PRACTICE MID-TERM EXAM

Multichoice Quetions 1. Atributes a. are listed in the second part of the class box b. its time is preceded by a colon. c. its default value is

EP241 Computer Programming

C++ INTERVIEW QUESTIONS

13 Classes & Objects with Constructors/Destructors

Introduction to C++ Introduction to C++ Week 7 Dr Alex Martin 2013 Slide 1

Name: Class: Date: 9. The compiler ignores all comments they are there strictly for the convenience of anyone reading the program.

Continued Fractions and the Euclidean Algorithm

Figure 2.1: Center of mass of four points.

CpSc212 Goddard Notes Chapter 6. Yet More on Classes. We discuss the problems of comparing, copying, passing, outputting, and destructing

Sources: On the Web: Slides will be available on:

Questions: Does it always take the same amount of force to lift a load? Where should you press to lift a load with the least amount of force?

Common Beginner C++ Programming Mistakes

Grade 7/8 Math Circles November 3/4, M.C. Escher and Tessellations

Cabri Geometry Application User Guide

Euler s Method and Functions

1 bool operator==(complex a, Complex b) { 2 return a.real()==b.real() 3 && a.imag()==b.imag(); 4 } 1 bool Complex::operator==(Complex b) {

2.2 Derivative as a Function

The Method of Least Squares. Lectures INF2320 p. 1/80

Linear Programming Problems

Moving from CS 61A Scheme to CS 61B Java

The Java Series. Java Essentials I What is Java? Basic Language Constructs. Java Essentials I. What is Java?. Basic Language Constructs Slide 1

DERIVATIVES AS MATRICES; CHAIN RULE

What are the place values to the left of the decimal point and their associated powers of ten?

Parametric Curves. (Com S 477/577 Notes) Yan-Bin Jia. Oct 8, 2015

Absolute Value Equations and Inequalities

= δx x + δy y. df ds = dx. ds y + xdy ds. Now multiply by ds to get the form of the equation in terms of differentials: df = y dx + x dy.

IS0020 Program Design and Software Tools Midterm, Feb 24, Instruction

Core Maths C1. Revision Notes

Solutions to Homework 10

7.6 Approximation Errors and Simpson's Rule

Chapter 4 OOPS WITH C++ Sahaj Computer Solutions

Area and Arc Length in Polar Coordinates

Definition: A vector is a directed line segment that has and. Each vector has an initial point and a terminal point.

CSCI 253. Object Oriented Programming (OOP) Overview. George Blankenship 1. Object Oriented Design: Java Review OOP George Blankenship.

The C Programming Language course syllabus associate level

Chapter 10. Key Ideas Correlation, Correlation Coefficient (r),

Nonlinear Algebraic Equations. Lectures INF2320 p. 1/88

Brent A. Perdue. July 15, 2009

Data Structures Using C++ 2E. Chapter 5 Linked Lists

Glossary of Object Oriented Terms

Chapter 7: Additional Topics

1.7 Graphs of Functions

Multi-variable Calculus and Optimization

Informatica e Sistemi in Tempo Reale

Chapter One Introduction to Programming

Linear Algebra Notes for Marsden and Tromba Vector Calculus

Generating Serialisation Code with Clang

LINEAR INEQUALITIES. Mathematics is the art of saying many things in many different ways. MAXWELL

Common Data Structures

Datum > Curve KIM,ME,NIU

Chapter 7 - Roots, Radicals, and Complex Numbers

Number Sense and Operations

G. GRAPHING FUNCTIONS

EQUATIONS and INEQUALITIES

2) Write in detail the issues in the design of code generator.

3 Contour integrals and Cauchy s Theorem

The programming language C. sws1 1

Trigonometric Functions and Triangles

Illustration 1: Diagram of program function and data flow

To give it a definition, an implicit function of x and y is simply any relationship that takes the form:

Circle Name: Radius: Diameter: Chord: Secant:

Java CPD (I) Frans Coenen Department of Computer Science

Symbol Tables. Introduction

17. Friendship and Inheritance

Scalar Valued Functions of Several Variables; the Gradient Vector

Object Oriented Software Design II

1. Relational database accesses data in a sequential form. (Figures 7.1, 7.2)

Geometric Transformations

10CS35: Data Structures Using C

Understanding Basic Calculus

Course Title: Software Development

Gerrit Stols

GeoGebra. 10 lessons. Gerrit Stols

Compiler Construction

the points are called control points approximating curve

Beginner s Matlab Tutorial

Homework 2 Solutions

C++ Programming Language

9.4. The Scalar Product. Introduction. Prerequisites. Learning Style. Learning Outcomes

Vector storage and access; algorithms in GIS. This is lecture 6

39 Symmetry of Plane Figures

1 if 1 x 0 1 if 0 x 1

Algebra I Notes Relations and Functions Unit 03a

1 Abstract Data Types Information Hiding

2008 AP Calculus AB Multiple Choice Exam

Chapter 5 Functions. Introducing Functions

Using Microsoft Word. Working With Objects

2.3 WINDOW-TO-VIEWPORT COORDINATE TRANSFORMATION

Coding conventions and C++-style

Note: Syntactically, a ; is needed at the end of a struct definition.

Object Oriented Software Design II

What is a parabola? It is geometrically defined by a set of points or locus of points that are

Transcription:

3 Representation in C++. The computer language C++ is well suited for implementing a grid generator. C++ is a superset of C. The discussion below assumes knowledge of the language C. One major difference between C++ and C is that C++ has a data type called a class. A class is a generalization of the struct in C, or the record in Pascal. In a class we can gather together a number of variables. We have also the possibility to let functions be a part of a class. Furthermore we can chose to make certain variables and functions inaccessible for all code outside the class itself. The main idea, is that a class should be something encapsulated well isolated from the rest of the code. The class can only be used through well defined interfaces. It is often difficult to design good classes, however for boundary curves it is fairly easy to get a clean representation with classes. 3.1 A first example of a class Below we show an example of a class which implements the boundary curve where x is used as curve parameter. class bcurve double a, b; double a0, a1; int dir; bcurve( double a in, double b in ) a0 = 0.3033; a1 = 0.67; a = a in; b = b in; dir = 0; double x( double p ) double y( double p ) return a0*sqrt(p) + a1*p; void reverse() dir =!dir; ; A class declaration has the structure class name // declarations ; y = a 0 x + a1 x, a x b (3.14) 32

The word class is a reserved word, the name of the class is an identifier chosen by the programmer. The class bcurve contains now the variables a,b, the end points of the curve parameter, a0, a1 describing the parameter functions, and dir which is the direction of the curve. The value 0 of dir means that p goes from a to b, avalueof1meansfromb to a. bcurve also contains the functions bcurve, x, y, and reverse. The reserved word private means that these variables can only be accessed from functions inside the class. Under public we declare the variables and functions which any user of the class will be able to access directly. This constitutes the interface to the class. A class should be designed such that specific details that we may want to change later is kept under private. Changes become in that case local only to the class, and the rest of the code using the class can remain unchanged. Another reason to use private, is to prevent users of the class to change variables by mistake. For example, the variable dir can not be accessed directly, but can only be changed in a very controlled way through the function reverse. The first function under public is a function with the same name as the class, and with no return type. This is an initialization function, called a constructor, which is called automatically each time a variable of the class type is created. The intended use of the constructor is to give values to all variables in the class, so that we do not risk, by mistake, to use uninitialized variables. The other two public functions are the parameterization (x(p),y(p)). In a main program, the class is used as in the example below, where 10 points on the curve are generated. // The class declaration here. int main() bcurve bc(0,1.0); double x[10], y[10]; for( int i=0; i<10; i++ ) x[i] = bc.x( i/9.0 ); y[i] = bc.y( i/9.0 );... ; A variable of class type, such as bc in the example above, is called an object. When the program is executed, the first declaration bcurve bc(0,1.0); will immediately cause the constructor of the bcurve class to be called with the parameter values a in=0, b in=1.0. Note the dot notation bc.x(0.0) to access a member function of a class. Variables are accessed in the same way, e.g., bc.a denotes the left endpoint of the curve. However, in this example, trying to access a in the main program will lead to a compilation error, because a is declared as private. Normally functions are not defined inside the class definition as in the example above. C++ allows us to define the functions outside the class, and only declare them inside the class. The same example can in that case be written class bcurve double a, b; double a0, a1; 33

int dir; bcurve( double a in, double b in ); double x(double p ); double y(double p ); void reverse(); ; bcurve::bcurve( double a in, double b in ) a0 = 0.3033; a1 = 0.67; a = a in; b = b in; dir = 0; double bcurve::x( double p ) double bcurve::y( double p ) return a0*sqrt(p) + a1*p; void bcurve::reverse() dir =!dir; When the function definitions are lifted out of the class, they must be preceded by classname::, so that the compiler knows where the functions belong. It is possible that two different classes contain functions with the same name. The function definitions can now be put anywhere in the program. Often they are written on a separate file. Every part of the code which will use the class bcurve must include the class declaration at the top of its file. Therefore one usually puts the class declaration in a special file called in this case bcurve.h which we give below. class bcurve double a, b; double a0, a1; int dir; bcurve( double a in, double b in ); double x(double p ); double y(double p ); void reverse(); ; The file bcurve.h The function definitions are put on a separate file, bcurve.c, which is compiled separately once and for all. We give bcurve.c below. #include "bcurve.h" bcurve::bcurve( double a in, double b in ) a0 = 0.3033; a1 = 0.67; a = a in; b = b in; dir = 0; 34

double bcurve::x( double p ) double bcurve::y( double p ) return a0*sqrt(p) + a1*p; void bcurve::reverse() dir =!dir; The file bcurve.c In the main program where we use the class bcurve, we include bcurve.h as shown below #include "bcurve.h" int main() bcurve lower boundary(0,0.5);... The advantage with this, is that the functions inside the class bcurve are compiled once. We can compile the main program without recompiling the functions in bcurve. Had they been written inside the class, as in the first example in this chapter, the function definitions would have been present in the include-file, and therefore recompiled every time the main program was recompiled. When several files include each other in a non-trivial way, it can easily happen that a file like bcurve.h becomes included more than once. To avoid this, it is common practise to put extra preprocessor directives in bcurve.h, as follows #ifndef #define BCURVE BCURVE class bcurve double a, b; double a0, a1; int dir; bcurve( double a in, double b in ); double x(double p ); double y(double p ); void reverse(); ; #endif The file bcurve.h, with preprocessor directives This will ensure that bcurve.h is included only once. One way to improve execution speeds is to inline small functions which are called often. Inlining means that the compiler does not generate calls to the function, but instead it inserts the entire source code of the function at each place where it is called. In C++, we tell the compiler to inline a function, by using the keyword inline in front of the function. inline double x( double p ) 35

Furthermore, all functions which are defined inside the class declarations becomes inline by default. Inlined functions are similar to macros, but gives less risk for unwanted side effects. Inlined functions must be defined in the same file as it is being called. Separate compiling of inline functions is not allowed in C++. Inline functions are therefore usually defined in the include file, such as shown in the class declaration bcurve.h below. #ifndef BCURVE #define BCURVE class bcurve double a, b; double a0, a1; int dir; bcurve( double a in, double b in ); inline double x(double p ) inline double y(double p ) return a0*sqrt(p) + a1*p; void reverse(); ; #endif We next improve the class above. Assume that we want to reparameterize, using the arc length normalized to [0, 1]. We would then define the class as class bcurve double a, b; double a0, a1; int dir; double length; double xo( double p ); double yo( double p ); bcurve( double a in, double b in ); double x( double s ); double y( double s ); ; The private functions xo,yo describes the curve in original parameterization, these are private because the user of the class should not have to worry about the exact description of the parameterization. Instead the curve should be accessed from the outside through the public functions x,y, in which the arc length is used as parameter. In this way, the curve can be implemented with any equivalent parameterization in xo,yo, the user will not note the difference in the functions x,y. Assume that we put the class declaration above on the file bcurve.h. The member functions can then be defined as below. #include "bcurve.h" double bcurve::xo( double p ) double bcurve::yo( double p ) return a0*sqrt(p) + a1*p; bcurve::bcurve( double a in, double b in ) 36

a0 = 0.3033; a1 = 0.67; a = a in; b = b in; dir = 0; b length = // numerical approximation of xo (p) 2 + yo (p) 2 dp a double bcurve::x( double s ) p // solve s =1/(length) xo (t) 2 + yo (t) 2 dt for p a return xo(p); double bcurve::y( double s ) p // solve s =1/(length) xo (t) 2 + yo (t) 2 dt for p a return yo(p); The main program given previously can still be used unchanged with this modified class. Assume that we want to use the parameter t instead of x, where the curve now is defined by x = t 2 y = a 0 t + a 1 t 2 This curve has the same graph as the curve (3.14), but a different parameterization (and different parameter values at the end points). We could then make this change in the functions xo,yo, but the main program (or any other function using the class) can remain unchanged. Remark: An efficient algorithm to solve the arc length equation s = 1 p x L (p) 2 + y (p) 2 dp a is Newton s method, p 0 =guess p k+1 = p k pk a x (p) 2 +y (p) 2 dp sl x (p k ) 2 +y (p k ) 2 k =0, 1, 2,... Where the iteration proceeds until the difference p k+1 p k is less than some given tolerance. The integral is evaluated by, e.g., Simpsons method, where the interval [a, p k ] is divided into a sufficiently large number of subintervals. It is possible to implement the integral evaluation such that only the update p k+1 p k.. dp needs to be computed in each iteration. However, I have not investigated whether this is advantageous or not. 3.2 Dynamic allocation of objects The declaration bcurve bc(0,1.0); allocates the object bc in static memory. It is common to use dynamic allocation of objects instead. In order to do that, we first declare a pointer to the object 37

bcurve *bc; The memory allocation is done by the statement bc = new bcurve(0,1.0); The parameters (0,1.0) are passed to the constructor, which is called automatically when the object is allocated. To access a member function of the object, we can use the expression x[i] = (*bc).x(i/9.0); The parenthesis are necessary, because the member selection operator,., has higher priority than the dereference operator, *. There is an alternative way to write the same expression, namely x[i] = bc->x(i/9.0); When an object is no longer needed, it can be deallocated by the delete operator, delete bc; Unused objects should be deallocated, since C++ does not have automatic garbage collection. Programs that break during execution due to memory leaks, are not uncommon with C and C++. 3.3 Base classes and virtual functions Consider the domain in Fig. 2.1 below. It consists of four sides, one is a circular arc, two sides are straight lines, and one side is defined by spline points. 2 1.5 1 0.5 0 0.5 1 1.5 2 2 1.5 1 0.5 0 0.5 1 1.5 2 Fig. 2.1 Example of boundary curves. We could think of introducing three C++ classes to describe the boundary. The classes circle do describe the arc, line to describe the straight line sides, and spline curve to describe the outer boundary. We could then write the class domain to describe the entire domain. It contains the four boundaries as members, and the class could have the capability to generate a grid on the domain. class domain circle *side1; spline curve *side2; line *side3, *side4; double *x, *y;... 38

... void generate grid( int n, int m );... ; The members x, y are matrices which will contain the grid. The problem with this approach is that it lacks generality. Tomorrow, we might want to generate a grid in a different domain. We would then have to rewrite the domain class, in order to fit in other types of boundary curves. We would like to have a way to write a general domain class in which we can declare the boundary curves as bcurve *sides[4]; and where the domain class does not need to care about which type of curve each side is. This can be accomplished by using inheritance, which we now describe by a simple example. Consider the class below, describing a polygon. #include <iostream.h> #include <math.h> struct point double x; double y; ; class polygon int npts; point *vertices; double distance( point, point ); polygon( int n, point *vert ); void translate( double dx, double dy ); void draw( Widget w, GC& gc ); void rotate( double alpha ); double perimeter(); ; We have introduced a data type point, describing a point in the plane. In C++ a struct is the same thing as class, except for the difference that all variables are public by default in a struct. In a class the default is to make a member private. For example, some of the member functions are defined as polygon::polygon( int n, point *vert ) npts = n; vertices = new point[npts]; for( int i=0 ; i<npts ; i++ ) vertices[i] = vert[i]; double polygon::perimeter( ) double p=0; for( int i=0 ; i<npts-1 ; i++ ) p += distance( vertices[i], vertices[i+1] ); p += distance( vertices[npts-1], vertices[0] ); The function distance gives the distance between two points in the plane. 39

Assume that we use this class to represent a square. The square has additional properties that makes it a very special type of polygon. For example, the perimeter of a square can be computed more efficiently than in the general case above, by just taking 4 times the side. We want to exploit the special properties of the square. We do that by introducing the class square as a derived class from the class polygon. The declaration of square is given below. class square : public polygon double side; square( point vert[4] ); double perimeter(); ; square::square( point vert[4] ) : polygon( 4, vert ) side = distance( vert[0], vert[1] ); double square::perimeter() return side*4; The first line class square : public polygon means that the square class contains the entire polygon class. We say that square inherits polygon. The public means that all the public members in polygon are also public in square. The constructor of square takes the four corner points as a parameter. Before the body of square::square is executed, the constructor of the base class polygon is called. This is the meaning of the notation : polygon(4,vert). Note how we have defined a new version of the function perimeter in square. In order for everything to work as intended we have to make a few small changes in the base class. We change the base class to class polygon protected: int npts; point *vertices; double distance( point, point ); polygon( int n, point *vert ); void translate( double dx, double dy ); void draw( Widget w, GC& gc ); void rotate( double alpha ); virtual double perimeter(); ; We have changed private to protected. The derived class, square, can not access the private variables in the base class. Protected is used to allow derived classes to access the variables. In other respects the variables under protected are inaccessible from the outside. The second change is that we have added the reserved word virtual to the function perimeter. The meaning of virtual is to tell the compiler that this is a function which can be redefined by a derived class. In the main program we can now use the classes as usual, point verts[5], sqverts[4];... polygon *p = new polygon( 5, verts ); 40

square *s = new square( sqverts ); What is more important is that we can assign squares to polygons, point verts[5], sqverts[4];... polygon *p = new polygon( 5, verts ); polygon *s = new square( sqverts ); // call to polygon::perimeter cout << "perimeter of polygon is " << p->perimeter() << endl; // call to square::perimeter cout << "perimeter of square is " << s->perimeter() << endl; If we had not declared perimeter to be a virtual function, the call s->perimeter above would have been a call to polygon::perimeter(). Although *s is of type polygon, it can be assigned a square. This is called polymorphism, and is a powerful technique, which is often used to avoid alternative statements like switch( type ) case s : per = perimeter square(); break; case r : per = perimeter rectangle();break; case c : per = perimeter circle();break; default: per = perimeter general(); Codes which rely on a special type variable to choose between cases, like in the example above, are difficult to maintain and modify. With a good hierarchy of inheriting classes such switch statements can be replaced by a single call to a virtual function. The assignment square s; polygon p; p = s; is thus allowed, but the opposite assignment s=p is forbidden. This is not strange, since it is unlikely that a polygon is a square. However, a square is always a polygon. 3.4 Abstract base classes In the previous example the function polygon::perimeter() was replaced by a function withthesamenameinthederivedclasssquare. It is often advantageous to instead define a base class with unspecified virtual functions, a so called abstract base class. Consider the example class shape points* vertices; int npts;... shape( ); virtual void rotate( double alpha ) = 0; virtual void translate( double dx, double dy ) = 0; virtual double perimeter() = 0;... ; By using the notation =0 after the function, we have here declared the functions rotate, translate, andperimeter to be purely virtual functions. This means that the the 41

functions are not defined in shape, but that they will be specified in classes which are derived from shape. No objects of the abstract base class can be created. The declaration of an object shape sh; is wrong, since the purely virtual functions are not defined in shape. An abstract class can be used only as a base for another class. An abstract class can be used to create an interface to the class without giving any information about implementation. 3.5 Programming exercise: A base class for boundary curves We would now like to use the techniques described above to redesign the bcurve and domain classes described in Section 3.3. First we define an abstract base class for boundary curves. It should contain at least the following information class curvebase protected: double pmin, pmax; // Max and minimum values of curve parameter int rev; // pmin to pmax or vice versa virtual double xp( double p ) = 0; // x(p) virtual double yp( double p ) = 0; // y(p) virtual double dxp( double p ) = 0; // x (p) virtual double dyp( double p ) = 0; // y (p) double integrate( double a, double b ); // arc length integral... curvebase() ; // constructor double x( double s ); // arc length parameterization double y( double s ); // arc length parameterization... ; Exercise 1: Complete the class by writing the non-virtual functions. Add more variables or functions to the class, if you find it necessary. Exercise 2: You will generate a grid on the domain in Fig. 2.2 below. 4 3 2 1 0 1 2 3 2 1 0 1 2 3 Fig. 2.2 Computational domain. The corners are located at (-3,1), (3,0), (3,3), and (-3,3). The lower boundary is given by the function y =1/(e 5x + 1). (The lower corners do not match perfectly, but for all practical purposes we can assume that 1/(e 15 +1)= 0and1/(e 15 +1) = 1.) Derive classes that are needed to represent the boundary curves of the domain in Fig. 2.2 from the base class. Test the class by using it in a simple main program. 42

Exercise 3: Design a class domain as outlined in Section 3.3. The class should contain four boundary curves of type curvebase, and have capability for generating a grid on the domain. Write a main program which generates the grid. Use the algebraic grid generation formula (2.2). Exercise 4: Add a function to the class domain to write the grid to a file. The simplest is to use cout to write an ascii file. A better way is to use the unix functions open, write, and close to output the grid in binary format. We give an example below of how they are used to write a vector x consisting of n m doubles. #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h>... int fd = open( "outfile.bin", O CREAT O RDWR O TRUNC, 0660 ); int nr = write( fd, x, n*m*sizeof(double) ); close(fd); Use the unix commands man open, andman -s 2 write to obtain more information about these functions. The grid can be viewed in Matlab. To read a binary file, use the Matlab functions fopen and fread. Exercise 5: This exercise is not compulsory, just do it if you have time, and would like to make a better program. The four boundary curves are somehow input to the class domain, e.g., through a constructor. You have probably up to now used some convention of type the first argument to the constructor should be the lower boundary etc. Add a function order boundaries to the class domain. This function should order a random set of four boundaries such that bcurve[0] and bcurve[1] are always facing each other, and have consistent coordinate directions, and the same for bcurve[2] and bcurve[3]. When this function is used, the boundary curves can be input to domain in any order, with any direction of the parameter. 43