with C++ Templates Templates Thomas Gschwind <thg at zurich dot - - PowerPoint PPT Presentation

with c templates
SMART_READER_LITE
LIVE PREVIEW

with C++ Templates Templates Thomas Gschwind <thg at zurich dot - - PowerPoint PPT Presentation

Advanced Software Engineering with C++ Templates Templates Thomas Gschwind <thg at zurich dot ibm dot com> Templates Polymorphisms Specialization Declaration and Use Classes and Members Ambiguities An Example


slide-1
SLIDE 1

Advanced Software Engineering with C++ Templates

Templates

Thomas Gschwind <thgatzurichdotibmdotcom>

slide-2
SLIDE 2

Templates

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

83

  • Polymorphisms
  • Declaration and Use
  • Ambiguities
  • Specialization
  • Classes and Members
  • An Example (pvector)
slide-3
SLIDE 3

Types of Polymorphisms

  • “Ad-hoc”
  • Overloading
  • Statically resolved by the compiler (using argument types)
  • Dynamic
  • Using virtual member functions
  • Method to be invoked identified during run-time

(using the virtual method table)

  • Static or Parametric
  • Using templates
  • Function to be invoked identified statically
  • Concrete Functions/Classes are generated for the individual parameter types
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

84

slide-4
SLIDE 4

Templates – Why?

  • Writing gcm, lcm, and swap for all kinds of types is tedious
  • We want to define the function once and use it for all types

possible

  • Sort of like Lisp, Smalltalk, Python, Ruby, you name it…
  • Just more efficiently
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

85

slide-5
SLIDE 5

Templates: Declaration and Definition

  • Allow the use of the same function/class for different types
  • The types become new compile-time parameters
  • Types need to implement the routines used by the template
  • The definition must be available to the compiler
  • Are checked and resolved statically (during compile time)
  • Function calls can be resolved during compilation time
  • Support generic programming
  • Many functions are the same independently of the data type
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

86

template<class T> inline T min(T a, T b) { return a<b ? a : b; }

This is “old style”, one should use typename instead but some people still prefer class. If you use an antiquated C++ compiler you may have to use class.

slide-6
SLIDE 6

Templates: Use

  • Are invoked like any other function (mostly)
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

89

template<typename T> inline T min(T a, T b) { return a<b?a:b; } const double pi=3.141596; void f() { min(2.718282, 1.0); min('a', 'z'); min(1, 26); min(pi, 2.718282); min('a', 26); min(2.718282, 1); }

slide-7
SLIDE 7

Templates: Use

  • Template parameters must be unambiguously resolved
  • Otherwise, the ambiguity needs to be resolved manually

template<typename T> inline T min(T a, T b) { return a<b?a:b; } const double pi=3.141596; void f() { min(2.718282, 1.0); min('a', 'z'); min(1, 26); min(pi, 2.718282); min('a', 26); min(2.718282, 1); } template<typename T> inline T min(T a, T b) { return a<b?a:b; } const double pi=3.141596; void f() { min(2.718282, 1.0); // ok min('a', 'z'); // ok min(1, 26); // ok min(pi, 2.718282); // ok min('a', 26); // error, ambiguous min(2.718282, 1); // error, ambiguous }

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

90

slide-8
SLIDE 8

Templates: Resolving Ambiguities

  • Unlike for “normal” functions,

there is no implicit conversion for templates

  • Explicit
  • If necessary

min<int>('a', 26);

  • Or even if unnecessary

min<const double>(pi, 2.718282);

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

92

slide-9
SLIDE 9

Mixing Templates and Non-Templates

  • Templates and non-templates can be mixed
  • Can define a template-based function min
  • And define a non template-based function min at the same time
  • Non-templates are preferred over templates if no type conversion

necessary

template<class T> inline T min(T a, T b) { return a<b ? a : b; } inline double min(double a, double b) { return a<b ? a : b; }

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

93

slide-10
SLIDE 10

Templates: Resolving Ambiguities (cont‘d)

  • We can create separate helper functions
  • Helper functions may be based on the underlying template
  • This approach not only looks tedious

but is also error-prone, clumsy, …

inline int min(int x, int y) { return min<int>(x,y); } inline double min(double x, double y) { return min<double>(x,y); }

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

94

slide-11
SLIDE 11

min Template – A Problem?

  • We have seen it works fine with numbers and characters
  • What about C-style strings?
  • The above will compare the addresses where the strings are stored
  • It will return the string with the smaller address
  • This is not what the typical developer wants …
  • For strings it would be better to use a lexicographical comparison

such as strcmp …

cout << min("Hello", "World") << endl;

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

95

slide-12
SLIDE 12

Specialization (1st Attempt)

  • Templates and non-templates can be mixed
  • Define a non template-based function min for C strings

template<typename T> inline T min(T a, T b) { return a<b ? a : b; } inline char *min(char *a, char *b) { return strcmp(a,b)<0 ? a : b; } inline const char *min(const char *a, const char *b) { return strcmp(a,b)<0 ? a : b; } #include "min.h" void foo(char *x, char *y, const char *z) { cout << min(x,y) << endl; cout << min(x,z) << endl; cout << min<const char*>(x,z) << endl; } // yes // yes // compiles but wrong We are asking for the template, so we get the template …

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

96

slide-13
SLIDE 13

Template Specialization (2nd and Final Attempt)

  • C++ allows us to specialize an existing template for specific types

template<typename T> inline T min(T a, T b) { return a<b ? a : b; } template<> inline char *min<char *>(char *a, char *b) { return strcmp(a,b)<0 ? a : b; } template<> inline char *min<const char *>(const char *a, const char *b) { return strcmp(a,b)<0 ? a : b; } #include "min.h" void foo(char *x, char *y, const char *z) { cout << min(x,y) << endl; // yes cout << min(x,z) << endl; // error cout << min<const char*>(x,z) << endl; // yes }

Compiler error; as we discussed, there is no implicit parameter conversions for templates.

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

97

slide-14
SLIDE 14

Templates: Classes and Members

  • Works exactly the same
  • Simply put

template <typename T, typename U, …>

in front of the declaration

  • It is even OK, to introduce new template parameters for individual

member functions

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

98

slide-15
SLIDE 15

A Persistent pvector Class

  • We want to implement a persistent version of C++’s vector class
  • Reads all elements from a file in the constructor
  • Writes all elements back to the file in the destructor

template<typename T> class pvector { string filename; vector<T> v; … public: pvector(string fname) : filename(fname) { readvector(); } ~pvector() { writevector(); } void push_back(const T &el) { v.push_back(el); } void pop_back() { v.pop_back(); } …

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

99

slide-16
SLIDE 16

A Persistent pvector Class (cont’d)

template<typename T> class pvector { string filename; vector<T> v; void readvector() { ifstream ifs(filename); for(;;) { T x; ifs >> x; if(!ifs.good()) break; v.push_back(x); } } void writevector() {

  • fstream ofs(filename);

typename vector<T>::iterator fst=v.begin(), lst=v.end(); while(fst!=lst) ofs << *fst++ << endl; } …

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

100

OR starting with C++11, simply: for (const T &elem : v) ofs << elem << endl;

slide-17
SLIDE 17

A Persistent pvector Class (cont’d)

  • What happens if we pass the pvector around?
  • Hence, maybe we want to disable the copy-constructor for

pvector<T>

void foo(pvector<int> pv) { if(pv.size()>0) cout << pv[0] << endl; pv.push_back(17); } int main(int argc, char *argv[]) { pvector<int> pv("/tmp/pvector-int.txt"); foo(pv); }

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

101

slide-18
SLIDE 18

Advanced Software Engineering with C++ Templates

Separate Compilation

Thomas Gschwind <thgatzurichdotibmdotcom>

slide-19
SLIDE 19

Separate Compilation

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

103

  • C++ versus Java
  • Variables
  • Routines (Functions & Operators)
  • Types (Structures, Classes)
  • Makefiles
slide-20
SLIDE 20

Separate Compilation

  • Why?
  • Having only one source file is unrealistic
  • Break the code up into its logical structure
  • Reduction of compile time
  • Only changed parts need to be recompiled
  • How?
  • Use multiple source files
  • Need to know what information about functions and variables

“used” from other files

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

104

slide-21
SLIDE 21

Separate Compilation in Java

  • Each Java file is compiled into a class file
  • If a Java class invokes a method of another class,

the compiler consults that other class file to

  • Determine whether the class provides the requested method
  • Determine whether a class file implements a given interface
  • etc.
  • Hence, the .class file contains the entire interface
  • That’s why in Java, the compiler needs the class path
  • Finally, all class files are loaded by the Java Virtual Machine

(and “linked”)

  • Java source code can be reconstructed from .class file

(see Java Decompiler: jd, jad)

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

105

slide-22
SLIDE 22

Some Java Trivia

  • Let us write Hello World in Java
  • In order to simplify the reconfiguration (e.g., translation)
  • Put all the constants into one Java file
  • Put the complex application code into another
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

106

public class Const { public static final String msg="Hello World!"; } public class Cool { public static void main(String[] args) { System.out.println(Const.msg); } }

slide-23
SLIDE 23

Some Java Trivia

  • Now compile both Java files and run Cool
  • Change the msg in Const.java, recompile Const.java, and run Cool

public class Const { public static final String msg="Hello World!"; } public class Cool { public static void main(String[] args) { System.out.println(Const.msg); } }

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

107

slide-24
SLIDE 24

Some Java Trivia

  • Now compile both Java files and run Cool
  • Change the msg in Const.java, recompile Const.java, and run Cool
  • Cool still prints the old message! Why?
  • javac inlines constants
  • And according to the Java specification that’s legal
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

108

public class Const { public static final String msg="Hello World!"; } public class Cool { public static void main(String[] args) { System.out.println(Const.msg); } }

slide-25
SLIDE 25

Separate Compilation in C++

  • By convention, each source file is compiled into an object file
  • Object files provide minimum necessary to execute the code
  • Object files do not provide enough information for the compiler to

identify

  • Functions provided by another compilation unit
  • Layout of a user-defined type
  • Object files are not used during the compilation
  • C++ and C use “header” files to store the interfaces of the object file
  • These files need to be supplied by the developer
  • In C/C++, we have the include path instead
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

109

slide-26
SLIDE 26

Header Files

  • C++ uses so-called header files for separate compilation
  • The header file can be viewed as the object file’s interface
  • Hence, header files are another encapsulation mechanism
  • They describe the interface(s) provided by the object files
  • Describe everything that should be exported to other compilation units
  • What goes into the header file?
  • “Everything” that should be exported (i.e., used in other files)
  • “Nothing” that causes the compiler to immediately generate code
  • Except, for a small number of exceptions
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

110

slide-27
SLIDE 27

Header Files: Prevent Multiple Inclusion (Guard)

  • Header files need protection from being included multiple times
  • Otherwise, this may cause compile errors
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

111

#include "vars.h" int my_dumb_global_variable=17; const double e=2.718281; #ifndef VARS_H_ #define VARS_H_ extern int my_dumb_global_variable; extern const double e; const double pi=3.141596; #endif

if VARS_H_ is not defined process the following lines define _VARS_H end the last open #if… section Process the file “vars.h” This pattern ensures that header files won’t be included multiple times

vars.h vars.cc

slide-28
SLIDE 28

Header Files: Prevents Multiple Inclusions (#pragma)

  • An alternative to the guard statement is to use #pragma once

in your header files

  • Although a non-standard, it is widely supported
  • Serves the same effect, less code to write
  • Can be more efficient during compilation, file needs to be opened only once
  • No name-clashes
  • Compiler may not always be able to identify whether two files are equal

(hard links, symbolic links, multiple filesystem mounts, etc.)

  • My suggestion
  • Stick to the standard
  • Use a system to uniquely generate the guard name
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

112

slide-29
SLIDE 29

Header Files: Variables

  • Variables
  • Declaration goes into the header

(if variable is to be accessed elsewhere)

  • Definition goes into the implementation file (allocates memory)
  • Constant Variables
  • Declaration goes into the header

(if variable is to be accessed elsewhere)

  • Definition goes either into the header or the implementation file
  • If in the header, definition may be allocated multiple times
  • No problems for individual values (constant and small)
  • Be careful with large constants such as large constant arrays
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

113

slide-30
SLIDE 30

Header Files: Variables Example

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

114

#include "vars.h" int my_dumb_global_variable=17; const int primes[]={2, 3, 5,…, 1234567891}; #ifndef VARS_H_ #define VARS_H_ extern int my_dumb_global_variable; const double pi=3.141596; extern const int[] primes; #endif

Use extern to declare a variable to be defined elsewhere. No memory will be allocated for the variable

vars.h vars.cc

Constants may be defined in the header or declared like other variables. Include the header (for consistency checking) Variables are defined in the implementation file. Do not repeat constants defined in the header.

slide-31
SLIDE 31

Header Files: Functions

  • Functions
  • The declaration of the function goes into the header
  • Definition goes into the implementation file
  • Inline Functions
  • If they are to be inlined in the corresponding implementation file only,

treat them like functions

  • If they are to be inlined globally (typical),

declaration and definition go into the header file

  • Necessar for the compiler to know

the implementation to be use instead of the function call

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

115

slide-32
SLIDE 32
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

116

Header Files: Functions Example

#include "util.h"

int gcf(int a, int b) {

if (a<b) swap(a,b); while (b!=0) { a=a-b; if (a<b) swap(a,b); } return a; }

#ifndef UTIL_H_ #define UTIL_H_

inline void swap(int &a, int &b) {

int c=a; a=b; b=c; }

extern int gcf(int a, int b); inline int lcm(int a, int b) {

return (a/gcf(a,b))*b; }

#endif

util.h util.cc

Inline functions are declared and defined in the header file. Use extern to declare a function. Extern for function declarations is

  • ptional. If there is no function

body, it cannot be a definition. Functions are defined in the implementation file. Do not repeat inline functions defined in the header.

slide-33
SLIDE 33

Header Files: Types (typedef, struct, class)

  • The type declaration and definition go into the header
  • The layout of the type needs to be known to the compiler

in all compilation units that need to allocate the type

  • For members of a type the same rules as for functions apply
  • Member declarations into the header
  • Member definitions (except for the ones to be inlined) into the

implementation file

  • All members of a class even if they should not be visible outside the

compilation unit need to be declared as part of the type definition

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

117

slide-34
SLIDE 34
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

118

Header Files: class Example

class fraction { int c; int d; public: fraction(int cntr=0, int denom=1) : c(cntr), d(denom) { /*void*/ } fraction operator*(const fraction &b); fraction operator/(fraction b) { swap(b.c, b.d); return (*this)*b; } }; #include "fraction.h" #include "util.h" fraction::fraction operator*(const fraction &b) { fraction r; int f1=gcf(this->c,b.d), f2=gcf(b.c,this->d); r.c=(this->c/f1)*(b.c/f2); r.d=(this->d/f2)*(b.d/f1); return r; }

  • perator/ is an implicitly inline

member, inline functions go into the header file.

fraction.h fraction.cc

The complete layout of a type (public, protected, private members) go into the header file.

slide-35
SLIDE 35
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

119

Header Files: Templates

  • Type declaration and definition go into the header
  • The code for a template is generated when it is parameterized
  • Hence, the compiler needs the full code to instantiate the template
  • If the template is only to be parameterized with a small set of types
  • Can treat template functions and template classes like normal functions and

classes

  • Need to instantiate the class in an implementation file that has access to the

full template definitions (for instance, template class pvector<string>;)

slide-36
SLIDE 36

main program

  • Include header files
  • System header files first allows to

find compile errors in header more easily

  • Own header files first allows to

find missing definitions in header more easily

  • Compile each file
  • Put dependencies into

Makefile

  • If implementation file changes, it

needs to be recompiled

  • If a header file changes, layouts
  • f types may have changed, all

files including it need to be recompiled

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

120

#include <stdlib.h> #include <iostream> #include "fraction.h" #include "util.h" #include "vars.h" void main(int argc, char *argv[]) { int arg=atoi(argv[1]); cout << arg << "^2*pi=“ << arg*arg*pi << endl; cout << "e=" << e << endl; cout << gcf(atoi(argv[1]), atoi(argv[2])) << endl; cout << lcm(atoi(argv[1]), atoi(argv[2])) << endl; … // use of fraction data type }

slide-37
SLIDE 37

Makefile

all: main main: main.o fraction.o util.o vars.o g++ -o main main.o fraction.o util.o vars.o main.o: main.cc fraction.h util.h vars.h g++ -c main.cc fraction.o: fraction.cc fraction.h util.h g++ -c fraction.cc util.o: util.cc util.h g++ -c util.cc vars.o: vars.cc vars.h g++ -c vars.cc

Makefile

main.cc fraction.cc fraction.h util.h vars.h main.o fraction.o util.o vars.o main util.cc vars.cc Link the final executable (could also use ldd but tedious)

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

121

slide-38
SLIDE 38

Useful Build Tools

  • gcc: not only compiles and links files
  • Also analyzes sources and generates dependencies between them

(Checkout the -M… options)

  • Also allows to analyze java files for the dependencies between them
  • ld: link object files
  • For C++ files, you need to include the C++ library as well (stdc++)
  • nm: list symbols in an object file or program
  • Defined, undefined, which section, etc.
  • ldd, otool –L: list libraries needed by an object file
  • Simply list the shared libraries the object file is dependent on
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

122

slide-39
SLIDE 39
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

123

Makefiles (made easy)

  • As mentioned before, gcc allows to generate source dependencies
  • This Makefile can be used as a generic starter for your Makefile

CXXFLAGS=... CXXFLAGS+=-Wall -Wextra -Werror OBJS=main.o # "main" file OBJS+=fraction.o ... # others… all: main clean: rm -f main *.o distclean: clean rm -f .depend/*.d rm -f *~ ...

slide-40
SLIDE 40
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

124

Makefiles (cont’d)

...

  • include $(addprefix .depend/,$(OBJS:.o=.d))

%.o: %.cc g++ $(CXXFLAGS) -c -o $@ $*.cc @g++ -MM $(CXXFLAGS) -c $*.cc >.depend/$*.d main: $(OBJS) g++ $(LDFLAGS) -o $@ $(OBJS)

slide-41
SLIDE 41

Advanced Software Engineering with C++ Templates

Memory Management

Thomas Gschwind <thgatzurichdotibmdotcom>

slide-42
SLIDE 42

Memory Management

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

126

  • Allocation & Deallocation
  • Stack
  • Variables (Pointers, Arrays,

References)

  • Heap
  • Memory and Classes
slide-43
SLIDE 43

Memory Management: Java vs. C++

  • In Java
  • Built-in types are stored on the stack
  • User-defined types are always stored on the heap

(only their references are stored on the stack)

  • Memory is automatically freed by the garbage collector
  • In C++
  • Built-in types and objects can be stored on the stack and heap
  • C++ supports references and pointers; both can refer/point to objects and

built-in types both on the stack and the heap

  • Stack memory is managed automatically
  • Heap memory needs to be allocated and freed explicitly

(with delete and delete[])

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

127

slide-44
SLIDE 44
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

128

Functions: Parameter Passing

  • Call by Value
  • Argument to be passed copied from the caller’s scope into the callee’s scope
  • Callee operates on its own copy
  • Call by Reference/Pointer
  • Callee receives a references to the argument passed by the caller
  • Callee operates on caller’s copy
  • C++/C
  • All parameters can be passed by value, by pointer, or in C++ also by reference
  • Java
  • Primitive types are passed by value
  • Class instances are passed by reference
  • C#
  • Value types (primitives and structs) by value or reference
  • Class instances by reference
slide-45
SLIDE 45

Memory Organization

  • The memory needs to store

different things

  • The program
  • Global variables
  • Local variables

(on the stack)

  • Etc.

Memory

0xffff:

. . . . . . Stack Program . . Global Variables . . .

0x0000:

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

129

slide-46
SLIDE 46

The Stack

  • Stores local variables, return addresses, etc.
  • Right side shows the (simplified) stack after

fact(2) has been invoked

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

130

int fact(int n) { int m=n-1; if (n<=2) return n; else return n*fact(m); } int main() { cout << "fact(4)=" << fact(4) << endl; return 0; }

0x0ff8: 0x0ffc: n==4, m==3 “line 8” . . main() - code . . 0x0ff0: “line 4” n==3, m==2 “line 4” n==2, m==1 0x0fec: 0x0fe8: 0x0fe4: 0x1000: result result result 0x0fdc: 0x0fd8: 0x0fd0: fact(4) fact(3) fact(2)

1 2 3 4 5 6 7 8 9 10

slide-47
SLIDE 47

Pointers (TYPE*)

  • Pointers are a fundamental concept of C++
  • Pointers should be used sparsely and carefully
  • A pointer points to a value or object stored anywhere in memory
  • Since pointers can point to different TYPEs of values, there are

different types of pointers denoted by TYPE*

  • A pointer is similar to an iterator iterating over a collection of

elements of type TYPE

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

131

slide-48
SLIDE 48

Pointer Operations

  • &-Operator
  • Obtain the address where a value is stored in memory (lvalue)
  • Returns a pointer to the type of the lvalue
  • &(lvalue)
  • *-Operator
  • Dereference a pointer (i.e., manipulate the memory that the pointer points

to)

  • Return the type of the value the pointer points to
  • *(expr)
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

132

An lvalue (left value) is an expression that can occur on the left or the right hand side

  • f an assignment expression. An rvalue is

an expression that can only occur on the right hand side.

slide-49
SLIDE 49

Pointers (cont’d)

  • Pointers store an address
  • This is visible to the developer
  • The type of the pointer indicates the

type of object stored at the address

Address-Space

0x0ff8: 0x0ffc: b==5 a==3 . . . . main() . . . . . . . 0x0ff4: pa==? a==9

int main() { int a=3; int b=5; int *pa; pa=&b; pa=&a; // ok *pa=9; // ok }

pa==0x0ff8 pa==0x0ffc 0x1000:

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

133

slide-50
SLIDE 50

Modifying Arguments with Pointers

  • Implement a routine that exchanges the value of two arguments
  • Arguments must be an lvalue
  • Invoked with c_swap(&var1, &var2);
  • In Java, this is impossible, arguments need to be wrapped within

an object

void c_swap(int *x, int *y) { int z=*x; *x=*y; *y=z; } int a=3, b=5; c_swap(&a, &b);

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

134

slide-51
SLIDE 51

Arrays

  • Arrays provide memory for several values of the same type
  • In C++ an array is typically equivalent to a pointer of the first

element of the array

0x0ff8: 0x0ffc: buf[2]==? buf[3]==? . . main() . . 0x0ff4: buf[1]==? buf[0]==? bufp==0x0fec 0x0ff0: 0x0fec: 0x0fe8: 0x1000:

void foo() { int buf[4]; int *bufp=buf; // ok buf[0]=3; // ok *buf=3; // same *bufp=*buf; // same ++bufp; *bufp=*buf+1; }

buf[0]==3 “line 8” bufp==0x0ff0 buf[1]==4

buf

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

135

slide-52
SLIDE 52

Arrays (Be Careful)

  • Arrays are not range checked (buffer-overflow)
  • C++ happily assigns a value to buf[4]
  • Developer has to keep track of this
  • There are several libraries providing a safe version of arrays (we’ll

come back to this)

  • Arrays “cannot” be returned as the result of a function
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

136

slide-53
SLIDE 53

References (TYPE&)

  • Similar to pointers – sometimes more elegant
  • However, like pointers they should be used
  • Sparsely and
  • Carefully
  • A reference refers to a value or object stored in memory, it is

another name (alias) for a given value or object stored in memory

  • Unlike a pointer, it cannot be changed to refer to a different

location

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

137

slide-54
SLIDE 54

References (cont’d)

  • References are similar to pointers
  • Except their implementation is

“invisible”

Address-Space

0x6f04: 0x6f08: b==5 a==3 . . . . main() . . . . . . . 0x6f00:

int main() { int a=3 int b=5; int &pa=a; pa=7; // ok //&pa=b; // error pa=b; // ok }

pa/a==3 pa/a==7 pa/a==5

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

138

slide-55
SLIDE 55

Modifying an Argument with References

  • Implement a routine that exchanges the value of two arguments
  • Argument must be an lvalue
  • Invoked with swap(var1,var2);
  • Similar to VAR parameter in Pascal
  • In Java, this is impossible, in C#, however, it is possible

void swap(int &a, int &b) { int c=a; a=b; b=c; }

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

139

slide-56
SLIDE 56

Returning References

  • Functions may also return references
  • Allows the function to be used as lvalue

class fraction { public: … // conversion fraction to double

  • perator double() { return (double)c/d; }

// references as return value int &counter() { return c; } int &denominator() { return d; } }; void normalize(fraction &a) { int f = gcf(a.counter(), a.denominator()); a.counter() = a.counter() / f; a.denominator() /= f; }

counter() and denominator() may now be used on the left side of operator= (i.e., as lvalue) However, this may make it harder to change the internal representation of fraction numbers in the future

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

140

slide-57
SLIDE 57

Use References with Care

  • References have the same characteristics as a pointer
  • The variable a reference point should exist as long as the reference itself
  • Never return a reference to a local variable!
  • BTW, avoid static local variables (they are like global variables)
  • References are taken implicitly
  • Makes their code nicer to read
  • BUT easy to overlook that they pose the same risks as pointers
  • May be generated “by accident”
  • The Google Coding Style suggests to avoid references as return

values and instead stick to pointers

  • Advantage, have to write return &variable;
  • Disadvantage, may not always be intuitive
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

141

slide-58
SLIDE 58

Commonly Used (Safe) Patterns

  • Function references elements passed to it by caller
  • swap(int &a, int &b)
  • If a and b are valid in caller scope, they “must be” valid in callee’s scope
  • Caveat: make sure that it is obvious to the caller that a and b are modified
  • print_vector(const vector<string> &v)
  • Almost as above, except with const we promise that v will not be modified
  • Avoids duplication of v which is otherwise passed by value (very common

pattern)

  • Function returns reference to element passed to it
  • ostream &operator<<(ostream &os, const T &element)
  • Same argument as above we return to caller a reference to an object that is his
  • Member function returns reference to element of its class
  • int &fraction::counter()

T &vector::operator[](size_t index)

  • Actually, same as above, since this is implicitly passed to the member function
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

142

slide-59
SLIDE 59

The Heap

  • As soon as data is allocated on the heap, think

about its ownership and life-cycle, whoever allocates it, should deallocate it

  • Stores non-local and non-global variables
  • If memory needs to be allocated

during runtime (e.g., linked lists, large arrays, …)

  • Memory needs to be explicitly allocated

(like new like in Java)

  • Useful for returning large user-defined types or

arrays (although consider a vector) from routines

Memory

0xffff:

. . . . . . Stack Program . Global Variables . Heap . .

0x0000:

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

143

slide-60
SLIDE 60

Memory Allocation & Deallocation

  • “No” garbage collection in C/C++
  • Memory needs to be allocated explicitly (new, new[]) and freed (delete,

delete[])

  • Some lazy and/or old-fashioned C programmers still use malloc when they

want uninitialized memory (there can be an advantage to this: realloc)

  • The proper way of allocating uninitialized memory, however, is

::operator new (size_t bytes)

  • Error handling
  • bad_alloc will be thrown (new …),
  • or NULL is returned (new (nothrow) …)
  • Initialization

“Unfortunately, overuse of new (and of pointers and references) seems to be an increasing problem.” Bjarne Stroustrup

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

144

slide-61
SLIDE 61

Returning an Array

  • An array per se is a pointer
  • Cannot return the entire array
  • Can only return the pointer

Memory

0xffff:

. . . .

0x0000:

int* foo2(int n) { int *buf=new int[n]; for(int i=0; i<n; ++i) { buf[i]=i*i; } return buf; } void main() { int *buf=foo2(10); for(int i=0; i<10; ++i) { cout << buf[i] << endl; } delete[] buf; }

buf[2]==4 buf[3]==9 buf[1]==1 buf[0]==0

Program .

0x1000: 0x2000: buf==? “line 7” buf==0x2000 0x0ff8: 0x0ff4: 0x0ff0: 0x2004: 0x2008: 0x200c: result

buf

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

145

slide-62
SLIDE 62

Returning an Array (Style!)

  • The example has a style problem
  • Can you spot it?
  • Memory is allocated in foo2 and deallocated in main
  • If possible one should deallocate memory in the same method as it is

allocated

  • What are the alternatives?
  • Allocate the array in main and pass it into foo2
  • If that is not possible implement a foo2_cleanup routine that deallocates the

memory returned by foo2

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

146

slide-63
SLIDE 63

Returning an Array with Nice Memory Management

  • Frequently, in C, when a function f allocates memory for us,

there is a sister function f’ that releases the memory allocated by f

(e.g., getaddrinfo, freeaddrinfo)

int* new_foo2(int n) {

int *buf=new int[n]; for(int i=0; i<n; ++i) { buf[i]=i*i; } return buf; }

void delete_foo2(int *buf) {

delete[] buf; } void main() {

int *buf=new_foo2(10);

for(int i=0; i<10; ++i) { cout << buf[i] << endl; }

delete_foo2(buf); }

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

147

slide-64
SLIDE 64

C++ auto_ptr<T> (deprecated since C++11)

  • C++ does not perform garbage collection but

has wrappers that come close

  • auto_ptr<T>

Delete object pointed to when ptr is destructed

void foo() { auto_ptr<int> pi (new int); *pi=17; cout << "*" << pi.get() << "=" << *pi << endl; auto_ptr<int> pj(pi); // transfer ownership; pi points to NULL *pj=19; cout << "*" << pi.get() << endl; // displays 0 cout << "*" << pj.get() << "=" << *pj << endl; } // deallocate the integer

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

148

Works the same with C++11 unique_ptr except use pj(pi.release()) to transfer ownership

slide-65
SLIDE 65

C++11: Memory Management

  • More versatile pointer wrappers
  • auto_ptr<T> depending on the context should be replaced with

unique_ptr<T> or auto_ptr<T>

  • The following “pointers” provided by the Standard Library help
  • unique_ptr<T>

Delete object pointed to when unique_ptr is destructed

  • shared_ptr<T>

Delete object pointed to by shared_ptr when this is the “last” pointer pointing to the object – works similar to the smart_ptr exercise

  • weak_ptr<T>

Useful in combination with shared_ptr if cyclic structures are used, allows to break up cycles, need to be converted to shared_ptr before object may be accessed, see documentation for details

  • Don’t use them as all-round solution
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

149

slide-66
SLIDE 66

C++11: Returning an Array

  • unique_ptr and shared_ptr can be used to return objects
  • The pointers will destruct the object when “they” no longer point

to it

unique_ptr<int[]> make_foo3(int n) { unique_ptr p{new int[n]}; for(int i=0; i<n; ++i) { p[i]=i*i; } return p; } void main() { auto buf = make_foo3(10); for(int i=0; i<10; ++i) { cout << buf[i] << endl; } }

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

150

slide-67
SLIDE 67

Frequently, a C++ vector will Work Best?

  • The previous code is great if you insist on C-style memory management
  • Why not simply return a vector<int>
  • No worries about memory management
  • More readable, more C++-like

vector<int> foo2(int n) {

vector<int> res(n); for(int i=0; i<n; ++i) { res[i] = i*i; } return res; } void main() {

vector<int> buf=foo2(10);

for(int i=0; i<10; ++i) { cout << buf[i] << endl;

} }

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

151

Creates n default ints; if that is a problem, use: vector<int> res; res.reserve(n); for(int i=0; i<n; ++i) { res.emplace_back(i*i); } return res;

slide-68
SLIDE 68

152

Summary

  • Templates
  • Definition
  • Use
  • Specialization
  • Separate Compilation
  • Memory Organization & Management
  • Allocation & Deallocation
  • Stack
  • Variables (Pointers, Arrays, References)
  • Heap
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.
slide-69
SLIDE 69

Exercise L2.1 [former L1.5]: pvector<T>

  • Implement the persistent vector data type.
  • Experiment with the persistent vector and use it in combination

with different data types. What do you observe? Why do you

  • bserve that behavior? How can it be changed?
  • What happens if we pass the pvector<T> around?
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

153

slide-70
SLIDE 70

Exercise L2.2

  • Upgrade your RPN calculator to add the following new

functionality:

  • It should be template enabled RPN<T>() will create your RPN calculator to

work with type T. Ensure it works with your fraction user defined type.

  • Make use of our pvector class to store the numbers the user pushes onto

the stack persistently.

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

154

slide-71
SLIDE 71

Exercise L2.3: Under the Hood

  • Have a look at the following swap routines
  • void swap(int &a, int &b) { int c=a; a=b; b=c; }
  • void c_swap(int *a, int *b) { int c=*a; *a=*b; *b=c; }
  • Let the compiler compile the code but ask the compiler to stop at

the assembly stage $ gcc -S -o source.s source.cc

  • Compare the assembly code, what do you observe?
  • How do you interpret the difference(s)?
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

155

slide-72
SLIDE 72

Exercise L2.4: Inline

  • Revisit your program that shows a function that cannot be inlined
  • Put the function to be inlined into a separate compilation unit
  • Once into the cc file as a normal function
  • Once into the header
  • Again, generate the assembly code for your program, what do you
  • bserver
  • Useful compiler options
  • -O0 asks the compiler to not optimize the output

(compiler doe not inline functions by itself)

  • -O3 asks the compiler to apply almost all optimizations

(compiler may even inline functions not marked as such)

  • -S asks the compiler to generate assembly code only
  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

156

slide-73
SLIDE 73

Exercise L2.5: Emulating pointers

  • Implement a dumb_pointer. Use the operator* and operator->
  • perators to implement a class that simulates a pointer.
  • Implement another class that works like the dumb_pointer but is

called smart_pointer and uses reference counting.

  • No, using any of the C++ auto_ptr, unique_ptr, shared_ptr,

weak_ptr classes is not an implementation option

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

158

slide-74
SLIDE 74

Exercise L2.5 (cont’d)

void print(smart_pointer<Object> p) { cout << p.counter() << ": " << *p << endl; } void foo() { Object *o1=new Object(1); // let's create our 1st object Object *o2=new Object(2); // let's create our 2nd object smart_pointer<Object> p(o1); // ref counter is 1 for 1st object cout << p.counter() << endl; // displays 1 smart_pointer<Object> q(p); // another smart pointer that points to o1 (overload copy constructor) cout << p.counter() << endl; // displays 2 (two smart pointers refer to o1) cout << q.counter() << endl; // displays 2 (two smart pointers refer to o1) smart_pointer<Object> r(o2); // ref counter is 1 for 2nd object cout << r.counter() << endl; // displays 1 q=r; // decrease counter for 1st object and // increase counter for 2nd object (overload assignment operator) cout << p.counter() << endl; // displays 1 cout << q.counter() << endl; // displays 2 cout << r.counter() << endl; // displays 2 print(p); // displays 2, and the 1st object, don't delete the object pointed to by p print(q); // displays 3, and the 2nd object, don't delete the object pointed to by q print(r); // displays 3, and the 2nd object, don't delete the object pointed to by r cout << *p << *r << endl; // display 1st and 2nd object (overload operator*) cout << p->method1() << q->method2() << r->method3() << endl; // invoke method1 on 1st object and // invoke method2 on 2nd object and // invoke method3 on 2nd object (overload operator->) } // now the destructors of p, q, and r are called, make sure that 1st // and 2nd object is each deleted once (i.e., when the counter reaches 0)

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

159

slide-75
SLIDE 75

Next Lecture

  • Standard Library II: Algorithms
  • Templates III: Implementation
  • Inheritance

Have a nice weekend, see you next week

  • Th. Gschwind. Advanced Software Engineering with C++ Templates.

160