• Keine Ergebnisse gefunden

Object-Oriented Programming for Scientific Computing

N/A
N/A
Protected

Academic year: 2021

Aktie "Object-Oriented Programming for Scientific Computing"

Copied!
35
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Object-Oriented Programming for Scientific Computing

Dynamic Memory Management

Ole Klein

Interdisciplinary Center for Scientific Computing Heidelberg University

ole.klein@iwr.uni-heidelberg.de

21. April 2015

(2)

Administrativa

Current situation:

• Registered students: 84 (75 BaMa / 9 PhD)

• Expected audience: 15 - 25 people

• Up to now only one tutor for the whole lecture

⇒seriously understaffed 6 credit points for everybody:

• Master students: lecture + exercises + final exam

• PhD students: lecture + exercises

(3)

Administrativa

In anticipation of a second tutor:

• A second exercise group slot is created, everybody from the Master group is moved to this slot

• You may freely choose, moving you just forces you to make an active decision

• The PhD group will be moved to the time slot with fewer Master students Capacity for correction is severely limited, therefore

• Youmusthand in the exercises in groups of three to four students (normally I would allow single hand in)

• Exercise submission only digitally via GitLab, will be explained on Thursday

• PhD students must hand in solutions, but these are only corrected if capacities allow

(4)

Organization of Memory

Static Memory

• Here global variables, variables belonging to a namespace and static variables are created.

• The memory is allocated when the program starts and is kept until the program ends.

• The addresses of variables in static memory don’t change while the program is running.

Stack (Automatic Memory)

• Here local and temporary variables are created (e.g. for function calls or return values).

• The allocated memory is automatically freed when the variable goes out of scope (e.g. when leaving the function in which it is defined).

• The size of the stack is limited (e.g. in Ubuntu by default 8192kb).

(5)

Organization of Memory

Heap (Free Memory)

• Can be requested by the program with the commandnew.

• Must be released with the commanddelete.

• Is in general limited only by the size of the main memory.

• May get lost due to programming errors.

(6)

Variables

• A variable designates a memory location in which data of a certain type can be stored.

• A variable has a name and a type.

• The amount of memory required for a variable depends on its type.

• The amount of memory that is required for a particular type of variable can be retrieved using the functionsizeof(variable type).

• Each variable has a memory address that can be queried with the address operator&.

• The address of a variable cannot be modified.

(7)

References

• A reference only defines a different name for an already existing variable.

• The type of the reference is the type of the variable followed by a&.

• A reference is initialized the moment it is defined and cannot be changed thereafter. It therefore always points to the same variable.

• A reference can be used in exactly the same way as the original variable.

• Modifications of the reference also change the content of the original variable.

• There can be multiple references to the same variable.

• A reference can also be initialized with a reference.

(8)

Example for References

# include< i o s t r e a m >

int m a i n () {

int a = 12;

int& b = a ; // d e f i n e s a r e f e r e n c e int& c = b ; // is a l l o w e d

f l o a t& d = a ; // is not allowed , t y p e m i s m a t c h int e = b ;

b = 2;

c = a * b ;

std :: c o u t < < a < < std :: e n d l ; std :: c o u t < < e < < std :: e n d l ; }

(9)

Pointers

• Pointers are a concept that is closely linked to the hardware.

• A pointer can store the address of a variable of a certain type or the address of a function.

• The type of a pointer variable is the type of the underlying variable followed by an asterisk*.

• Pointer contain memory addresses of variables, changing the pointer changes the memory location it points to.

• If one wants to access the value at that memory address, one places a* in front of the name of the pointer.

• If a pointer points at an object and one wants to access the attributes or methods of the object, one can use the operator->. The expressions *a.value

anda->valueare equivalent.

(10)

Pointers

• If a pointer is not initialized during its definition, then it just points at a random memory location.

• If a pointer points to a memory location that wasn’t assigned to the program by the operating system and reads or writes to the value at that address, then the program will terminate with an error message namedsegmentation fault.

• To clearly mark a pointer as not pointing to a variable or function one assigns the value0. In C++11 the special keywordnullptrcan be used for this.

• This makes it simple to test whether a pointer is valid.

(11)

Pointers

• There are also pointers pointing to pointers, e.g.

int a = 2;

int* b = & a ; int** c = & b ;

• The increment and decrement Operators++/--increase a pointer not by one byte, but by the size of the variable type to which the pointer points (the pointer then points to the “next” element) .

• If a numberi is added/substracted from a pointer, then the memory address changes byi times the size of the variable to which the pointer points.

(12)

Example for Pointers

# include< i o s t r e a m >

int m a i n () {

int a = 12;

int* b = & a ; // d e f i n e s a p o i n t e r to a

f l o a t* c ; // d e f i n e s a p o i n t e r to f l o a t s ( p o i n t i n g // to s o m e w h e r e u n s p e c i f i e d )

d o u b l e* d = n u l l p t r ; // b e t t e r t h i s way f l o a t e ;

c = & e ;

* b = 3; // m o d i f i e s v a r i a b l e a b = & e ; // not allowed , w r o n g t y p e

e = 2 * * b ; // allowed , e q u i v a l e n t to * c = 2 * a std :: c o u t < < b < < std :: e n d l ;

b = b + a ; // is allowed , but r i s k y

// b now p o i n t s to a n o t h e r m e m o r y c e l l std :: c o u t < < a < < std :: e n d l ;

std :: c o u t < < d < < std :: e n d l ; std :: c o u t < < b < < std :: e n d l ; }

(13)

Arrays in C (and C++)

• Arrays in C are closely related to pointers.

• The name of an array in C is also a pointer to the first element of the array.

• The use of the bracket operatora[i] corresponds to a pointer operation

*(a+i)

# include< i o s t r e a m >

int m a i n () {

int n u m b e r s [ 2 7 ] ;

for (int i = 0; i < 2 7 ; ++ i ) n u m b e r s [ i ] = i * i ; int* end = n u m b e r s + 26;

for (int* c u r r e n t = n u m b e r s ; current <= end ; ++ c u r r e n t ) std :: c o u t < < * c u r r e n t < < std :: e n d l ;

}

(14)

Risks of Pointers

While dealing with pointers and arrays in C/C++, there are two major threats:

1 A pointer (particularly in the use of arrays) will be modified (accidentally or on purpose), so that it points to memory areas which haven’t been allocated.

At best, this leads to closing of the program due to asegmentation fault. In the worst case it can be used to gain access to the operating system.

2 Data is written beyond the end of an arry. If the affected memory was allocated by the program (because other variables are stored in that location), this often leads to very strange errors, because these other variables suddenly contain wrong values. In large programs the exakt spot where this happens may be hard to find.

(15)

Call by Value

If an argument is passed to a function, then a local copy on the stack is created for this argument with each function call.

• If a normal variable is in the argument list, then a copy of this variable is generated.

• This is calledCall by Value.

• Modification of the variables within the function doesnotchange the original variable where the function was called.

• If large objects are passed this way, then generating this copy can become very expensive (running time, memory requirements).

d o u b l e S q u a r e C o p y (d o u b l e x ) {

x = x * x ; r e t u r n x ; }

(16)

Call by Reference

• If a reference or a pointer is in the list of argument, then copies of the reference or of the pointer can be generated. These still point to the same variable.

• This is calledCall by Reference.

• Changes in the contents of the reference or the memory cell to which the pointer points effect the original variable.

• This allows writing functions that return more than one value and functions with an effect but without return value (procedures).

• A constant reference, e.g.double Square(const double &x), can be used to pass large objects as an argument while preventing modification of the original.

v o i d S q u a r e (d o u b l e & x ) {

x = x * x ; }

(17)

Dynamic Memory Management

Large objects, or arrays with a size that is determined during runtime, can be allocated on the heap with the help ofnew.

c l a s s X {

p u b l i c:

X () ; // c o n s t r u c t o r w i t h o u t a r g u m e n t s X (int n ) ; // w i t h an int a r g u m e n t

...

};

X * p = new X ; // c o n s t r u c t o r w i t h o u t a r g u m e n t s X * q = new X ( 1 7 ) ; // w i t h an int a r g u m e n t

...

(18)

Dynamic Memory Management

Objects which are produced withnewdon’t have a name, only an address in memory. This has two consequences:

1 The lifetime of the object isn’t fixed. The programmer must destroy it explicitly with the commanddelete:

d e l e t e p ;

This can only be done once per reserved object.

2 In contrast, the pointer used to access this object usually has a limited lifespan.

⇒Object and pointer must be managed consistently.

(19)

Possible Problems

1 The pointer no longer exists, but the object is still existing⇒memory is lost, the program gets bigger and bigger (memory leak).

2 The object is no longer existing, but the pointer does⇒accessing the pointer creates asegmentation fault. Especially dangerous when several pointers point at the same object.

These two issues will be addressed by smart pointers introduced later in the lecture.

(20)

Allocating Arrays

• Arrays are allocated by writing the number of elements in brackets behind the type of variable.

• Arrays can only be allocated if the class has a constructor without arguments.

• Arrays are deleted withdelete []. The implementation ofnew []and

delete []may by incompatible with that ofnewanddelete, e.g. in some implementations the length of the array is stored before the data and a pointer pointing to the actual data is returned.

int n ;

std :: cin > > n ; // u s e r e n t e r s d e s i r e d l e n g t h of a r r a y X * pa = new X [ n ];

...

d e l e t e [] pa ;

⇒One must not mix the different forms ofnewanddelete. For individual variablesnewanddeleteare used, and for arraysnew []anddelete [].

(21)

Releasing Dynamically Allocated Memory

• Calingdeleteordelete []for a pointer that points to a location that has already been freed or wasn’t reserved results in asegmentation fault.

• Passing a null pointer todeleteanddelete []is harmless.

• The C memory commandsmalloc andfree should not be used in C++

programs.

(22)

Classes with Dynamically Allocated Members

Wrapping the dynamic memory management with a class definition

• Can hide the details of dynamic memory usage from the users

• Fixes (if correctly programmed) some of the major disadvantages of dynamically allocated memory in C

Issues of raw pointers that are addressed:

• Call by value becomes possible

• Objects are able to know their size

• If an object is destroyed, the destructor can automatically release dynamically allocated memory

(23)

Example: Matrix Class with Dynamic Memory

• The data is stored in a two-dimensional dynamically allocated array.

• Instead of the vector of vectors, the matrix class receives a pointer to a pointer ofdoubleas private menber.

d o u b l e ** a_ ; int n u m R o w s _ ; int n u m C o l s _ ;

• Methods to implement: constructor(s), destructor, copy constructor, assignment operator

(24)

Constructors

M a t r i x C l a s s () : a_ (0) , n u m R o w s _ (0) , n u m C o l s _ (0) {};

M a t r i x C l a s s (int dim ) : a_ (0) {

R e s i z e ( dim , dim ) ; };

M a t r i x C l a s s (int numRows , int n u m C o l s ) : a_ (0) {

R e s i z e ( numRows , n u m C o l s ) ; };

M a t r i x C l a s s (int numRows , int numCols , d o u b l e v a l u e ) : a_ (0) {

R e s i z e ( numRows , numCols , v a l u e ) ; };

(25)

Resize Methods

v o i d M a t r i x C l a s s :: R e s i z e (int numRows , int n u m C o l s ) {

D e a l l o c a t e () ;

a_ = new d o u b l e*[ n u m R o w s ];

a_ [0] = new d o u b l e[ n u m R o w s * n u m C o l s ];

for (int i =1; i < n u m R o w s ;++ i ) a_ [ i ]= a_ [ i - 1 ] + n u m C o l s ; n u m C o l s _ = n u m C o l s ;

n u m R o w s _ = n u m R o w s ; }

v o i d M a t r i x C l a s s :: R e s i z e (int numRows , int numCols , d o u b l e v a l u e ) {

R e s i z e ( numRows , n u m C o l s ) ; for (int i =0; i < n u m R o w s ;++ i )

for (int j =0; j < n u m C o l s ;++ j ) a_ [ i ][ j ]= v a l u e ;

}

(26)

Destructor

~ M a t r i x C l a s s () {

D e a l l o c a t e () ; };

p r i v a t e:

i n l i n e v o i d D e a l l o c a t e () {

if ( a_ ! = 0 ) {

if ( a_ [ 0 ] ! = 0 )

d e l e t e [] a_ [ 0 ] ; d e l e t e [] a_ ;

} }

(27)

Copy Constructor and Assignment Operator

The default versions of copy constructor and assignment operator create a direct copy of all the variables. This would mean that now two pointers point to the same dynamically allocated data.

M a t r i x C l a s s (c o n s t M a t r i x C l a s s & b ) : a_ (0) {

R e s i z e ( b . n u m R o w s _ , b . n u m C o l s _ ) ; for (int i =0; i < n u m R o w s _ ;++ i )

for (int j =0; j < n u m C o l s _ ;++ j ) a_ [ i ][ j ]= b . a_ [ i ][ j ];

}

M a t r i x C l a s s &o p e r a t o r=(c o n s t M a t r i x C l a s s & b ) {

R e s i z e ( b . n u m R o w s _ , b . n u m C o l s _ ) ; for (int i =0; i < n u m R o w s _ ;++ i )

for (int j =0; j < n u m C o l s _ ;++ j ) a_ [ i ][ j ]= b . a_ [ i ][ j ];

r e t u r n *t h i s; }

(28)

Further Adjustments

The bracket operators still need to be adapted (actually this only affects the return type). The parenthesis operators require no changes:

d o u b l e *o p e r a t o r[](int i ) ;

c o n s t d o u b l e *o p e r a t o r[](int i ) c o n s t;

The implementation of matrix-vector product and Gauss algorithm for this variant of the matrix class is omitted.

(29)

Static Variables

• Sometimes classes have members which exist only once for all objects of the class.

• These variables are of typestatic, e.g.static int max.

• In a program there is exactly one version of a static member (not one version per object), and memory for the member is only occupied once.

• Methods that don’t work with the data of a specific object (i.e. use at most static variables) can also be defined as static member functions.

• Prefixing the name of the class followed by two colons, one can access the static attributes and methods without creating a temporary object.

• (Non-constant) static attributes must be initialised outside of the class.

(30)

Static Variables

# include< i o s t r e a m >

c l a s s N u m e r i c a l S o l v e r {

s t a t i c d o u b l e t o l e r a n c e ; p u b l i c:

s t a t i c d o u b l e G e t T o l e r a n c e () {

r e t u r n t o l e r a n c e ; }

s t a t i c v o i d S e t T o l e r a n c e (d o u b l e tol ) {

t o l e r a n c e = tol ; }

};

d o u b l e N u m e r i c a l S o l v e r :: t o l e r a n c e = 1 e -8;

int m a i n () {

std :: c o u t < < N u m e r i c a l S o l v e r :: G e t T o l e r a n c e () < < std :: e n d l ; N u m e r i c a l S o l v e r :: S e t T o l e r a n c e (1 e - 1 2 ) ;

std :: c o u t < < N u m e r i c a l S o l v e r :: G e t T o l e r a n c e () < < std :: e n d l ; }

(31)

C++11 and Dynamic Memory Management

Temporary Objects

Problem: If a value is e.g. returned by a function, temporary objects may be created. The following function may create up to two temporary objects when it returns:

d o u b l e S q u a r e C o p y (d o u b l e x ) {

r e t u r n x * x ; }

• A temporary object stores the result ofx*x.

• Since this object is created inside the function and will be deleted when the function exits, a copy of the return value is generated.

Copying large amounts of data can be quite time consuming. This is for the most part optimized by C++ compilers (return value optimisation, RVO).

(32)

C++11 and Dynamic Memory Management

Move Constructors

Idea: Since the temporary objects are directly destroyed after use, it isn’t

necessary to copy the data. It can be “recycled” by other objects. (There are also other applications). In C++11, there are explicit constructs for this:

• Move constructors and move-assignment operators reuse the contents of another (usually temporary) object. The members of this other object are replaced with default values (which are cheap to produce).

• This is applicable during initialization of objects, for the transfer of function arguments and for return values of functions.

• If the object is not temporary, the compiler needs to be explicitly informed that resources can be acquired. This is done with the keywordstd::move(), e.g.

M a t r i x C l a s s a ( 10 ,1 0 ,1 .0 ) ;

M a t r i x C l a s s b = std :: m o v e ( a ) ; // now b is a 10 x10 M a t r i x std :: vector <double> x ( 1 0 , 1 . 0 ) ;

x = b . S o l v e ( std :: m o v e ( x ) ) ; // c a l l of the f u n c t i o n

(33)

Efficient Swap Using Move Semantics

The following code snippet copies a presumably large matrix three times:

v o i d s w a p ( M a t r i x C l a s s & a , M a t r i x C l a s s & b ) {

M a t r i x C l a s s tmp ( a ) ; // c r e a t e s c o m p l e t e c o p y

a = b ; // as a b o v e

b = tmp ; // as a b o v e

}

All three lines copy from a location that is overwritten or discarded later on. Move semantics can be used to avoid the expensive copies:

v o i d s w a p ( M a t r i x C l a s s & a , M a t r i x C l a s s & b ) {

M a t r i x C l a s s tmp ( std :: m o v e ( a ) ) ; // u s e s the m e m o r y of a

a = std :: m o v e ( b ) ; // u s e s the m e m o r y of b

b = std :: m o v e ( tmp ) ; // u s e s the m e m o r y of tmp

}

(34)

• Move constructors (and move-assignment operators) are automatically created in C++11 if for a user-defined class no constructor, move

constructor, assignment operator or destructor has been defined and if it is trivial to generate a move constructor.

• In other cases, the generation of a default move constructor or assignment operator follows the same rules as for normal constructors with the keyword

default, e.g.:

M a t r i x C l a s s ( M a t r i x C l a s s &&) = d e f a u l t;

(MatrixClass &&is a so-called r-value reference, which can only refer to temporary objects or objects marked withstd::moveand which was first introduced in C++11)

• A move constructor is trivial when:

The class pocesses neither virtual functions nor virtual base classes.

The move constructor for each direct base class of the class is trivial.

The move constructor of all non-static attributes is trivial.

• All standard data types which are compatible with C are trivially movable.

• The move concept works not only for memory but also for other resources, such as files or communicators.

(35)

Summary

• Memory is divided into three parts, static, automatic (Stack) and dynamic (Heap)

• References and pointers are two different ways of indirection when dealing with variables

• Pointers are more flexible but also much more dangerous

• Hiding dynamic memory management inside classes avoids pitfalls and reduces complexity

• C++11 introduces move semantics that reduce the number of unnecessarily created temporary variables

Referenzen

ÄHNLICHE DOKUMENTE

replace_copy(b,e,out,v,v2) Create copy of all elements in range [b:e) re- placing elements which are equal to v with v2 , return iterator to end of copy.

• Traits can be used to specify types and values that depend on one or more template parameters. • Policies can be used to specify parts of algorithms as

Since the units are only used as a template parameter, this only affects the class type but does not require memory... Example: Numbers

This header also contains functions that provide a thread ID and the functionality to detach threads that afterwards run as a separate program (fork).. Mutual Exclusion The header

and no exercise group, because things are still being set up, but you are welcome to attend if you have questions about the lecture or exercises or something else to discuss.. E

~List (); // clean up the list and all nodes Node* first() const; // return a pointer to the first entry Node* next( const Node* n) const; // return a pointer to the node after n

Please modify your implementation again to obtain a doubly linked list: each element should also point to its predecessor.. What is

To understand why the size of empty classes (according to standard) is as observed, consider the following class!. struct