c++: memory management, summary
DESCRIPTION
Christian Schulte [email protected] Software and Computer Systems School of Information and Communication Technology KTH – Royal Institute of Technology Stockholm, Sweden. C++: Memory Management, Summary. ID1218 Lecture 10 2009-11-30. Overview. Memory management case studies - PowerPoint PPT PresentationTRANSCRIPT
C++: MEMORY MANAGEMENT, SUMMARY
ID1218 Lecture 10 2009-11-30
Christian [email protected]
Software and Computer SystemsSchool of Information and Communication
TechnologyKTH – Royal Institute of Technology
Stockholm, Sweden
Overview Memory management case studies
resizable arrays freelists reference counting
Summary C and C++ summary… … and questions you should be able to answer
L10, 2009-11-30ID1218, Christian Schulte
2
Case studies
Memory Management
L10, 2009-11-30
3
ID1218, Christian Schulte
Memory Management How to…
fit C++ structure, example: resizable arrays make efficient, example: free lists
Who does what? who allocates? who is responsible for deallocation? example: reference counting
L10, 2009-11-30ID1218, Christian Schulte
4
Resizable Arrays
L10, 2009-11-30
5
ID1218, Christian Schulte
Resizable Arrays Should behave like an array
the number of elements can be changed provides correct construction, deletion,
assignment supports array access operator [] can be passed as constant reference polymorphic with respect to element type can be mixed with "normal" arrays
L10, 2009-11-30ID1218, Christian Schulte
6
Resizable Arrays Members
current size: unsigned int element array: *Element
First attempt for integers make polymorphic in last step
L10, 2009-11-30ID1218, Christian Schulte
7
Resizable Arrays: Classclass IRA {private: unsigned int _n; int* _x;public: IRA(unsigned int n=8); IRA(const IRA& ira); IRA& operator=(const IRA& ira); void resize(unsigned int n); int get(unsigned int i) const; void set(unsigned int i, int x); ~IRA();};
L10, 2009-11-30ID1218, Christian Schulte
8
Constructorclass IRA {private: …public: IRA(unsigned int n=8) : _n(n), _x(new int[_n]) {} …}; Any member function defined inside class is
inline Member functions can also be defined outside
L10, 2009-11-30ID1218, Christian Schulte
9
Inline Definitionclass IRA {private: …public: IRA(unsigned int n=8); …};inlineIRA::IRA(unsigned int n) : _n(n), _x(new int[_n]) {} Any member function defined inside class is inline Member functions can also be defined outside
L10, 2009-11-30ID1218, Christian Schulte
10
Outline Definition Header file:
class IRA {private: …public: IRA(unsigned int n=8);…};
Implementation fileIRA::IRA(unsigned int n) : _n(n), _x(new int[_n]) {}
L10, 2009-11-30ID1218, Christian Schulte
11
Copy ConstructorIRA::IRA(const IRA& ira) : _n(ira._n), _x(new int[_n]) { for (unsigned int i=0; i<_n; i++) _x[i] = ira._x[i];}
Design: nothing shared between copiesL10, 2009-11-30ID1218, Christian Schulte
12
DestructorIRA::~IRA() { delete [] _x;}
Design: nothing shared between copies
L10, 2009-11-30ID1218, Christian Schulte
13
Assignment OperatorIRA&IRA::operator=(const IRA& ira) { if (this != &ira) { delete [] _x; _n = ira._n; _x = new int[_n]; for (unsigned int i=0; i<_n; i++) _x[i] = ira._x[i]; } return *this;}
Design: nothing shared between copies
L10, 2009-11-30ID1218, Christian Schulte
14
Accessinline intIRA::get(unsigned int i) const { return _x[i];}inline voidIRA::set(unsigned int i, int x) { _x[i]=x;}
Must go into header fileL10, 2009-11-30ID1218, Christian Schulte
15
Access: Assert Proper Useinline intIRA::get(unsigned int i) const { assert(i < _n); return _x[i];}inline voidIRA::set(unsigned int i, int x) { …}
Checked at runtime requires #include <cassert> runtime error, if violated (can be looked for in debugger) ignored, if the macro NDEBUG is defined pass –DNDEBUG from commandline
L10, 2009-11-30ID1218, Christian Schulte
16
Resizing the ArrayvoidIRA::resize(unsigned int n) { int* x = new int[n]; unsigned int m = std::min(n,_n); for (unsigned int i=0; i<m; i++) x[i] = _x[i]; delete [] _x; _n = n; _x = x;}
std::min requires #include <algorithm>L10, 2009-11-30ID1218, Christian Schulte
17
Overloading []: First Attemptclass IRA {private: unsigned int _n; int* _x;public: IRA(unsigned int n=8); IRA(const IRA& ira); IRA& operator=(const IRA& ira); void resize(unsigned int n); int operator[](unsigned int i); ~IRA();};
L10, 2009-11-30ID1218, Christian Schulte
18
Overloading []: First Attemptinline int IRA::operator[](unsigned int i) { assert(i < n); return _x[i];} Does not work: ira[7] = 4;
must pass a reference instead Does not work: ira[7] if ira is const IRA&
must also provide const version
L10, 2009-11-30ID1218, Christian Schulte
19
Overloading []: Second Attempt
class IRA {private: unsigned int _n; int* _x;public: IRA(unsigned int n=8); IRA(const IRA& ira); IRA& operator=(const IRA& ira); void resize(unsigned int n); int& operator[](unsigned int i); const int& operator[](unsigned int i) const; ~IRA();};
L10, 2009-11-30ID1218, Christian Schulte
20
Overloading []: Second Attempt
inline int& IRA::operator[](unsigned int i) { assert(i < n); return _x[i];} Works for: ira[7] = 4;
passes a reference to position in array
L10, 2009-11-30ID1218, Christian Schulte
21
Overloading []: Second Attempt
inline const int& IRA::operator[](unsigned int i) const {
assert(i < n); return _x[i];}
Works for ira[7] if ira is const IRA& provides const version
L10, 2009-11-30ID1218, Christian Schulte
22
Generic Resizable Arraystemplate <typename T>class RA {private: unsigned int _n; T* _x;public: RA(unsigned int n=8); RA(const RA<T>& ra); RA<T>& operator=(const RA<T>& ra); void resize(unsigned int n); T& operator[](unsigned int i); const T& operator[](unsigned int i) const; ~RA();};
L10, 2009-11-30ID1218, Christian Schulte
23
Generic Copy Constructortemplate <typename T>RA<T>::RA(const RA<T>& ra) : _n(ra._n), _x(new T[_n]) { for (unsigned int i=0; i<_n; i++) _x[i] = ra._x[i];} Define in header file What is executed:
default constructor? yes! copy constructor? no! assignment operator? yes!
L10, 2009-11-30ID1218, Christian Schulte
24
Mixing With Normal ArraysRA<int> x(42);void f(int* a);void g(int a[]);f(x); g(x);
Both calls do not work: x is of type RA<int>
Simple solution: f(&x[0]); g(&x[0]);
L10, 2009-11-30ID1218, Christian Schulte
25
Mixing With Normal Arraystemplate <typename T>class RA { …public: … operator T*(void) { return _x; }}; Dangerous…
RA<int> x(42); int* y = x;x.resize(1024);y[0] = 4;
L10, 2009-11-30ID1218, Christian Schulte
26
Freelists
L10, 2009-11-30
27
ID1218, Christian Schulte
Efficient Lists Common behavior
create and delete list cells very often Memory allocation and deallocation is
expensive Idea
store allocated but unused list cells in a freelist allocate from freelist whenever possible deallocate to freelist
L10, 2009-11-30ID1218, Christian Schulte
28
Integer Listsclass IL {private: int _hd; IL* _tl;public: IL(int h, IL* t = NULL); int hd() const; void hd(int h); IL* tl() const; void tl(IL* t);}; Basic required functionality
no default constructor, no … Naïve memory policy: delete single list cell
refine, if different policy needed
L10, 2009-11-30ID1218, Christian Schulte
29
Integer ListsinlineIL::IL(int h, IL* t) : _hd(h), _tl(t) {}inline intIL::hd() const { return _hd; }inline voidIL::hd(int h) { _hd=h; }inline IL*IL::tl() const { return _tl; }inline voidIL::tl(IL* t) { _tl=t; }
L10, 2009-11-30ID1218, Christian Schulte
30
Overloading Memory Allocation#include <cstdlib>class IL {private: int _hd; IL* _tl;public: IL(int h, IL* t = NULL); int hd() const; void hd(int h); IL* tl() const; void tl(IL* t); static void* operator new(size_t s); static void operator delete(void* p);};
L10, 2009-11-30ID1218, Christian Schulte
31
Overloading Memory Allocationinline void*IL::operator new(size_t s) { return malloc(s);}inline voidIL::operator delete(void* p) { free(p);}
Same behavior as beforeL10, 2009-11-30ID1218, Christian Schulte
32
Freelist as Static Member#include <cstdlib>class IL {private: int _hd; IL* _tl; static IL* _free;public: IL(int h, IL* t = NULL); int hd() const; void hd(int h); IL* tl() const; void tl(IL* t); static void* operator new(size_t s); static void operator delete(void* p); static void flush();};
L10, 2009-11-30ID1218, Christian Schulte
33
Implementation File: BrokenIL* IL::_free = NULL;voidIL::flush() { for (IL* f=_free; f!=NULL; f=f->_tl) free(f); _free = NULL;} Freelist is initially empty Access after free!
L10, 2009-11-30ID1218, Christian Schulte
34
Implementation File: BetterIL* IL::_free = NULL;voidIL::flush() { IL* f=_free; while (f!=NULL) { IL* t = f->_tl; free(f); f = t; } _free = NULL;}
L10, 2009-11-30ID1218, Christian Schulte
35
Allocating from the Freelistinline void*IL::operator new(size_t s) { if (_free == NULL) { return malloc(s); } else { IL* il = _free; _free = _free->_tl; return il; }}
Same behavior as beforeL10, 2009-11-30ID1218, Christian Schulte
36
Deallocating to the Freelistinline voidIL::operator delete(void* p) { IL* il = static_cast<IL*>(p); il->_tl = _free; _free = il;}
L10, 2009-11-30ID1218, Christian Schulte
37
Generic Freelists Not straightforward
no static members in parametric classes Solution
implement type independent freelist manager pass freelist manager as extra argument to
new and delete (placement new and delete) read a book…
L10, 2009-11-30ID1218, Christian Schulte
38
Reference Counting
L10, 2009-11-30
39
ID1218, Christian Schulte
Sharing Arrays Desired functionality
resizable array without copies (use a lot of memory) same array
What memory management policy unshared arrays: whoever created array is
responsible (directly or indirectly) to delete shared array: when is the array not any longer
used?
L10, 2009-11-30ID1218, Christian Schulte
40
Reference Counting Separate
array proper: store elements handle: implement allocation protocol
provide access to array proper Store in array proper: reference count
…how many handles refer to array array creation through handle: just one when array becomes shared through additional
handle: increment when handle is deleted: decrement if reference count hits zero: delete array
L10, 2009-11-30ID1218, Christian Schulte
41
Array Object Properclass AO {public: unsigned int n; int* x; unsigned int rc;};
L10, 2009-11-30ID1218, Christian Schulte
42
Array Handleclass AH {private: AO* _ao;public: AH(unsigned int n=8); AH(const AH& ah); AH& operator=(const AH& ah); ~AH(); int& operator[](unsigned int i); …};
L10, 2009-11-30ID1218, Christian Schulte
43
Array Handle ConstructorAH::AH(unsigned int n) : _ao(new AO) { _ao->n = n; _ao->x = new int[n]; _ao->rc = 1;}
L10, 2009-11-30ID1218, Christian Schulte
44
Array Handle: Object Accessinline int&AH::operator[](unsigned int i) { return _ao->x[i];}…
L10, 2009-11-30ID1218, Christian Schulte
45
Array Handle DestructorinlineAH::~AH() { _ao->rc--; if (_ao->rc == 0) { delete [] _ao->x; delete _ao; }} Only if no more handles refer to object, delete object Different design: provide constructor and destructor
for array objectL10, 2009-11-30ID1218, Christian Schulte
46
Copy ConstructorinlineAH::AH(const AH& ah) : _ao(ah._ao) { _ao->rc++;}
Array object is used by one additional handle
L10, 2009-11-30ID1218, Christian Schulte
47
Assignment OperatorinlineAH::operator=(const AH& ah) { if (this != &ah) { if (--_ao->rc == 0) { delete _ao->x; delete _ao; } _ao = ah._ao; _ao.rc++; } return *this;} Unsubscribe handle from current object
possibly delete object Subscribe to new object
L10, 2009-11-30ID1218, Christian Schulte
48
Putting the Object at its Place…class AH {private: class AO { public: unsigned int n; int* x; unsigned int rc; }; AO* _ao;public: AH(unsigned int n=8); …}; Object should never be used without controlling handle
L10, 2009-11-30ID1218, Christian Schulte
49
Smart Pointers Generic wrapper class overloading
operators -> and * Part of C++ standard library Use reference counting inside
L10, 2009-11-30ID1218, Christian Schulte
50
Problems With Reference Counting
Cost every assignment and copy is expensive all access has to go through one additional
indirection Cyclic datastructures
suppose reference counted lists create cyclic list all reference counts in cycle will be at least one never deleted
L10, 2009-11-30ID1218, Christian Schulte
51
Summary: Basics
L10, 2009-11-30ID1218, Christian Schulte
52
L10, 2009-11-30ID1218, Christian Schulte
Integer Types Basic integer type int
no guarantee on range in C++ today, often 32 bits, anytime soon 64 bits, … depends on machine (think of embedded…)
Can be augmented with short or long int never shorter than short int (at least 16 bits) int never longer than long int (at least 32 bits)
Can be modified with signed (default) or unsigned
short signed int -32768 … 32767 short unsigned int 0 … 65535 just for example!
53
L10, 2009-11-30ID1218, Christian Schulte
Floating Point Types Single precision float
often: 32 bits Double precision double
often: 64 bits
54
L10, 2009-11-30ID1218, Christian Schulte
Character Type Character type char
no guarantee on size, often 8 bits (ASCII, not Unicode!)
unspecified whether signed or unsigned
Character constants in single quotes 'a', '1' escape sequences: '\n' for newline, etc
Also available: wchar_t for wide characters
55
L10, 2009-11-30ID1218, Christian Schulte
Boolean Type Boolean type bool
constants true and false
Rather late addition! watch out for int with zero as false and any
other value true!
56
L10, 2009-11-30ID1218, Christian Schulte
Constants Variables taking an immutable value
cannot be changed abbreviation for commonly used values
Use const modifierconst double pi = 3.1415;
assignment to pi not possible
57
L10, 2009-11-30ID1218, Christian Schulte
Operators and Expressions No left to right order
readInt() – readInt()with input 2 and 3 can be either 1 or -1
Integer division not guaranteed to round towards zero (book uses down)
8/-5can be either -1 or -2
Comma operator: a,b evaluates to b weird things possible!
58
L10, 2009-11-30ID1218, Christian Schulte
Unitialized Variables Variables not by guarantee initialized
unless global or static
Always give initial value important for objects
59
L10, 2009-11-30ID1218, Christian Schulte
Static Casts Cast at least one into double
static_cast<double>(x)/y;
Other casts dynamic_cast casting objects, later const_cast later reinterpret_cast just do it as long size
fits… C-style cast ((double) x)
dangerous, combines everything, don't use!
60
L10, 2009-11-30ID1218, Christian Schulte
Example Questions What is the relation between
sizeof(int) sizeof(short int) sizeof(long int)
Answersizeof(short int)
sizeof(int)sizeof(int) sizeof(long int)
61
Summary: Arrays and Pointers
L10, 2009-11-30ID1218, Christian Schulte
62
L10, 2009-11-30ID1218, Christian Schulte
C-style Arrays C-style arrays
int a[42];creates an array of 42 integers
access cout << a[1]; assignment a[1] = a[2]+a[3]; ranges from a[0] to a[41]
Dimension of array must be constant can be evaluated at compile time to constant
(eg 2*4) illegalint a[n] where n is variable!
63
L10, 2009-11-30ID1218, Christian Schulte
Arrays are Pointers C-Style arrays are basically pointers
point to beginning of array Array access a[i] translates to *(a+i) Common idiom: pointer arithmetic
pointer +/- integer: offset to pointer pointer – pointer: distance between pointers
64
L10, 2009-11-30ID1218, Christian Schulte
Creating Dynamic Arrays An array of size n
int* a = new int[n];
Release memory laterdelete [] a;
never forget the []: important for arrays of objects (calling destructor)
65
L10, 2009-11-30ID1218, Christian Schulte
Example Questions Is this legal?
int n = 6;int a[n];
const int n = 5;int b[n*4];
Answer no yes
66
L10, 2009-11-30ID1218, Christian Schulte
Example Questions Value of x and y…
int a[] = {4,6,8}; int b[] = {1,3,5};int x = *(a+5-*(b+1));*(b+a[1]-a[0]) = *a; int y = *a+*(b+(&b[2]-&b[0]));
value of x: *(a+5-*(b+1)) = a[5-b[1]] = a[5-3] = a[2] = 8
assignment: b[a[1]-a[0]] = a[0], that is b[2]=a[0]
value of y: a[0] + b[b+2-b] = a[0] + b[2] = 4 + 4 = 8
67
Summary: Functions
L10, 2009-11-30ID1218, Christian Schulte
68
L10, 2009-11-30ID1218, Christian Schulte
Functions: Issues Overloading Declarations Definitions Default parameters Inline functions Call-by-value and call-by-reference
69
L10, 2009-11-30ID1218, Christian Schulte
Example Questions Is this legal?
int f(int);double f(double x) { return x+1.0; } yes, absolutely!
70
L10, 2009-11-30ID1218, Christian Schulte
Example Questions What is the value of z?
int f(double x) { return static_cast<int>(x)-1; }int f(int* x) { return *(x+1); }int f(int x, int y=4) { return x+y-1; }int a[] = {1,2,3};int z = a[f(1.0)]-f(1)+f(7,5)+f(&a[1]); f(1.0) = 0 f(1) = 1+4-1 = 4 f(7,5) = 7+5-1 = 11 f(&a[1]) = *(a+2) = 3
71
Summary: Objects and Classes
L10, 2009-11-30ID1218, Christian Schulte
72
L10, 2009-11-30ID1218, Christian Schulte
Accessors vs Mutators Fundamental difference
change object state: mutator do not change state: accessor
Accessors need to be declared const required for passing by const reference
73
L10, 2009-11-30ID1218, Christian Schulte
Initializer Lists In constructor after parameter list
after colon: comma separated list in order of declaration of members
Only initialize, avoid initialization with subsequent assignment
74
L10, 2009-11-30ID1218, Christian Schulte
Copying and Assignment Copying is controlled by copy constructorIntCell(const IntCell& c) : x(c.x) {}
Assignment is controlled by assignment operatorIntCell& operator=(const IntCell& c) { if (this != &c) x=c.x; return *this;}
These are synthesized by compiler if missing required for resource management
75
L10, 2009-11-30ID1218, Christian Schulte
Destructor~IntCell() { delete x;}
When object is deleted by delete (for heap allocated) by going out of scope (for automatically allocated)
destructor is invoked for resource management
76
L10, 2009-11-30ID1218, Christian Schulte
Default Constructor A constructor with no arguments (or all
arguments with default values) automatically generated, if no constructors
provided
Important for initializationIntCell c;
invokes the default constructor
77
L10, 2009-11-30ID1218, Christian Schulte
Example Find three mistakes and correct
class A {private: int* x;public: A() : x(new int[1]) {} ~A() { delete x; } int val() { return *x; } A(const A& a) : x(new int[1]) { *x = a.val(); }
A& operator=(const A& a) { delete [] x; x = new int[1]; *x=a.val(); return *this; }};
78
L10, 2009-11-30ID1218, Christian Schulte
Example Find three mistakes and correct
class A {private: int* x;public: A() : x(new int[1]) {} ~A() { delete x; } int val() { return *x; } A(const A& a) : x(new int[1]) { *x = a.val(); }
A& operator=(const A& a) { delete [] x; x = new int[1]; *x=a.val(); return *this; }};
const missing!
79
L10, 2009-11-30ID1218, Christian Schulte
Example Find three mistakes and correct
class A {private: int* x;public: A() : x(new int[1]) {} ~A() { delete x; } int val() { return *x; } A(const A& a) : x(new int[1]) { *x = a.val(); }
A& operator=(const A& a) { delete [] x; x = new int[1]; *x=a.val(); return *this; }};
must be: delete [] x
80
L10, 2009-11-30ID1218, Christian Schulte
Example Find three mistakes and correct
class A {private: int* x;public: A() : x(new int[1]) {} ~A() { delete x; } int val() { return *x; } A(const A& a) : x(new int[1]) { *x = a.val(); }
A& operator=(const A& a) { delete [] x; x = new int[1]; *x=a.val(); return *this; }};
test for self-assignment missing
81
L10, 2009-11-30ID1218, Christian Schulte
Example What is the value of z?
class A {public: int x; A() : x(7) {} A(int y) : x(y+4) {} A(const A& a) : x(a.x-1) {} A& operator=(const A& a) { x = a.x+3; return *this; }};A a(4); A b; A c(a); b = c; int z = b.x; a.x = 8, c.x = 7, b.x = 10
82
Summary: Fine Points for Objects
L10, 2009-11-30ID1218, Christian Schulte
83
L10, 2009-11-30ID1218, Christian Schulte
Static Members Static member is part of class, not of
instance maintain data that is shared among all
instances of a class Requires declaration and definition Declaration
use keyword static done in header
Definition use class name done in implementation
84
L10, 2009-11-30ID1218, Christian Schulte
Namespaces Organize multiple functions, classes, etc
togethernamespace N {… }
Inside the namespace, as alwaysC
Outside namespaceN::C
Convenience: make available single class using N::C; namespace using namespace N;
Reference to C in global namespace::C
85
Summary: Inheritance
L10, 2009-11-30ID1218, Christian Schulte
86
L10, 2009-11-30ID1218, Christian Schulte
Static versus Dynamic Dispatch Static dispatch: use compile-time type of
reference to object default behavior in C++
Dynamic dispatch: use runtime type for finding member function
default behavior in Java in C++: mark member functions as virtual important case: virtual destructor
87
L10, 2009-11-30ID1218, Christian Schulte
Defaults with Inheritance Copy constructor
invokes copy constructor on base classes invokes copy constructor on newly added data
members Assignment operator
invokes assignment operator on base classes invokes assignment operator on newly added data
members Destructor
invokes destructors on newly added data members invokes destructors on base classes
88
L10, 2009-11-30ID1218, Christian Schulte
Abstract Methods and Classes Member functions can be declared
abstract in base class declare as virtual declaration followed by = 0;
Must be implemented in inheriting classes
instance creation only for-non abstract classes possible
89
L10, 2009-11-30ID1218, Christian Schulte
Type Conversions Assume we know that p is a student,
reallyPerson* p = new
Student(123,"Eva",2); Use dynamic castStudent* s = dynamic_cast<Student*>(p);
performs check and cast at runtime returns NULL, if not a Student
90
L10, 2009-11-30ID1218, Christian Schulte
Example What is the value of z?
class A { public: virtual int x() { return 1; } int y() { return -1; } };class B : public A { public: virtual int x() { return 6; } };class C : public B { public: int y() { return 3; } };class D : public B { public: virtual int x() { return B::x()+8; } }; C* c = new C(); D* d = new D();int z = c->y()+dynamic_cast<A*>(d)->x()+dynamic_cast<A*>(c)->y();
dynamic_cast<A*>(d)->x() use dynamic dispatch and returns 6+8=14
dynamic_cast<A*>(c)->y() uses static dispatch and returns -1
91
Summary: C Programming
L10, 2009-11-30
92
ID1218, Christian Schulte
L10, 2009-11-30ID1218, Christian Schulte
Major Differences… No classes
use structs (see later) No references
use pointers No overloading No templates No default parameters All local variable declarations must be at
beginning of function Memory management Libraries
93
L10, 2009-11-30ID1218, Christian Schulte
The Struct Type Combine data into one structure
struct IntPair {int x; int y;
}; Also available in C++, corresponds to
class IntPair {public:
int x; int y;};
94
L10, 2009-11-30ID1218, Christian Schulte
Preprocessor Macros Conditional compilation Textual substitution
#define FOOVALUE 1 Macros
#define max(x,y) (x>y?x:y) unsafe! unsafe! things go wrong
b = max(a--,c--);
95
L10, 2009-11-30ID1218, Christian Schulte
C-Style Memory Management Allocate n bytes from heap
void* malloc(size_t n); Free memory block p obtained by malloc
void free(void* p); Never mix new+delete with malloc+free
never use delete on malloc block and vice versa
96
Outlook
L10, 2009-11-30
97
ID1218, Christian Schulte
Outlook Discussion
functional programming concurrent programming object-oriented programming imperative programming system programming
Course summary
L10, 2009-11-30ID1218, Christian Schulte
98