c - universität · pdf filewill: c ++11 programmieren, 2012, 978-3-8362-1732-3 iso/iec...

77
++

Upload: phamhanh

Post on 09-Mar-2018

218 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

C++

Dipl. Math. F. Braun

Universität Regensburg � Rechenzentrum

svn/doku/trunk/cpp/cpp.tex

http://www.uni-regensburg.de/EDV/kurs_info/brf09510/kurs_info/cpp/cpp.html

http://www.uni-regensburg.de/EDV/kurs_info/brf09510/kurs_info/cpp/cpp.pdf

12. April 2018

Page 2: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem
Page 3: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

KAPITEL 1

Einführung in C++

1. Kursübersicht

ausgeteilt:

2. Designer

Stroustrup, AT&T

3. Literatur und Internet

Frank B. Brokken: C++ Annotations, 10.9.2, 2018, 90-367-0470-7http://www.icce.rug.nl/documents/cplusplus/

Ulrich Breymann: Der C++-Programmierer, Carl Hanser, 4. Au�age, 2015, 3-446-44346-0

Nicolai M. Josuttis: The C++ Standard Library, Addison Wesley Longman, 2012, 0-321-62321-5

David Vandevoorde, Nicolai M. Josuttis: C++ Templates, Addison-Wesley, 2002, 0-201-73484-2

Scott Meyers: E�ective Modern C++ , C++11 and C++14, O'Reilly, 2014, 9781491903995Weitere Titel: E�ective C++, More E�ective C++, E�ective STL,

Herb Sutter: Exceptional C++ , 1999, 0201615622

Paul Deitel, Harvey Deitel: C++11 for Programmers, 2014, 978-0-13-343985-4 (Detailliert; rigider coding standard)

Lippman, Lajoie, Moo: C++ Primer, 2013, 978-0-321-71411-4

Torsten T. Will: C++11 programmieren, 2012, 978-3-8362-1732-3

ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee - ISOCPPhttp://www.open-std.org/JTC1/SC22/WG21/

ISO/IEC 14882, 2014-12-15, C++

Standardisierunghttps://isocpp.org/std/status

Compiler-Standhttp://en.cppreference.com/w/cpp/compiler_support

4. Geschichte

1956-1968 Fortran, 1960 Algol, 1960 Cobol, 1967 Pascal, 1965-67 PL/1, 1968 Algol-681965-1993 Simula, 1980 Smalltalk, 1980 Ada, 1985-87 Oberon, 1986 Ei�el, 1993 Java1970 C, Kernighan, Ritchie1980 C with classes, Bjarne Stroustrup, AT&T1982 ACM Sigplan notizes Vol 17 nr 11983/84 C++: der Name steht fest1985 erster Compiler: CFront1986 Stroustrup: The C++ programming language ("Bible")1989 Ansi-C (C89, C90)1989 ANSI Komitee ISO/IEC JTC1, Subkomitee SC221989 C++ 2.0, CFront 2.0

3

Page 4: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

4 1. EINFÜHRUNG IN C++

1990 Java1990 Ellis/Stroustrup: The Annotated C++ Reference Manual1993/94 Stepanov/Musser: STL Proposal1995 C++ Draft: ISO Entwurf1996 CD2 Second commenting draft1998-9-1 C++98 ISO/IEC 14882:1998 Standard mit der STL1999 C991999 Boost Library2003 C++ (03) ISO/IEC 14882:2003 (2. edition)2006 ISO/IEC TR 18015:2006 C++ Performance2007 ISO/IEC TR 19768:2007 C++ Library2010 ISO/IEC TR 29124:2010 C++ Library: Mathematical functions2011 C112011-9-01 C++11 ISO/IEC 14882:2011 (3. edition)2011 ISO/IEC TR 24733:2011 C++ Decimal Floating Point Arithmetic2012 The Standard C++ Foundation2014-12-15 C++14 ISO/IEC 14882:2014 (4. edition)2015 ISO/IEC TR 18822:2015 C++ The Filesystem Library2015 ISO/IEC TR 19570:2015 C++ Parallelism2015 ISO/IEC TR 19841:2015 C++ Transactional Memory2015 ISO/IEC TR 19568:2015 C++ Library Fundamentals2015 ISO/IEC TR 19217:2015 C++ Concepts2016 ISO/IEC TR 19571:2016 C++ Concurrency2017 N4659 �nal Draft http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf2017-12 C++17 ISO/IEC 14882:2017 (5. edition)2020 C++20

5. Ziele und Eigenschaften

C++ sollte ein ungeändertes, verbessertes C mit Objektorientierung werden. Es wurzelt in den Sprachen C, Small-talk und Ada. Schon kurz nach den ersten Entwürfen wurde es verstärkt in der professionellen Software-Produktioneingesetzt. Von dieser Seite wurden die Ziele schnell - korrekt - ökonomisch vorgegeben. Objektorientierung istDatenkapselung, Zugri�s-Rechte und Vererbung. Industriell ist graphische Software-Entwicklung mit UML vonBedeutung. C++ hat ein Standbein in der HPC-Programmierung. Verbreitete Libraries erleichtern die Software-Entwicklung.

6. Compiler und Tools

6.1. Arbeiten auf Servern. Zum Arbeiten auf Servern (pc55556) wird ssh/scp oder unter Windows putty-/pscp benötigt.

Installation: http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html

6.2. JEdit und IDEs. Als Programm-Editor wird JEdit empfohlen. Software-Entwicklung mit IDEs wieEclipse in Projekten unterstützt C++.

Webseite: http://www.jedit.org/Download: http://www.jedit.org/index.php?page=download

6.3. Gnu. Aufruf: g++Manual: https://gcc.gnu.org/onlinedocs/gcc/index.htmlStatus: https://gcc.gnu.org/projects/cxx-status.htmlStatus Library: https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html

Page 5: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

7. DIE BEZIEHUNGSKISTE VON C UND C++ 5

6.4. LLVM. Aufruf: clang++Manual: http://clang.llvm.org/docs/UsersManual.htmlStatus: http://clang.llvm.org/cxx_status.htmlStatus Library: https://libcxx.llvm.org/

6.5. Intel. Aufruf: ipcpManual: https://software.intel.com/en-us/compiler_15.0_ug_cStatus: https://software.intel.com/en-us/articles/c0x-features-supported-by-intel-c-compilerStatus Library: https://software.intel.com/en-us/articles/c14-features-supported-by-intel-c-compiler

6.6. Portland group. Aufruf: pgc++Manual: http://www.pgroup.com/doc/pgiug.pdf

6.7. MS Visual C++. Aufruf: cl /EHsc hello.cppManual: https://blogs.msdn.microsoft.com/vcblog/2014/11/17/c111417-features-in-vs-2015-preview/

6.8. Online Compiler. Es gibt mehrere online-Compiler, die Übersetzung und Start von kleineren C++-Programmen ermöglichen. Sie erlauben vor allem das Ausprobieren aktuellster Sprachkonstrukte.

https://isocpp.org/get-started Linkshttps://isocpp.org/blog/2013/01/online-c-compilers Linkshttp://coliru.stacked-crooked.com/ Coliruhttp://ideone.com/zjXGRo Ideonehttp://comeaucomputing.com/tryitout/ Comeauhttp://gcc.godbolt.org/ godbolt; kurze maximale Compile-Zeithttp://melpon.org/wandbox/ Wandbox

6.9. Debugger. Fehlerhafte Programmen werden mit Debuggern wie gdb und idb analysiert. Werkzeuge wielint/splint existieren in C++ kaum.

6.10. Make, cmake. Zur schnellen und bequemen Übersetzung und Montage von Programmen aus mehrerenDateien.

6.11. Libraries. http://de.cppreference.com/w/cpp/links/libs

http://en.cppreference.com/w/cpp/links/libs

https://github.com/fffaraz/awesome-cpp

https://stackoverflow.com/questions/777764/what-modern-c-libraries-should-be-in-my-toolbox

STL: Klassische objektorientierte Library; angepasst an C++; Teil des Standards seit 1998.

Boost: Freie C++-Quellcode Libraries (peer-reviewed); insgesamt über 80 Themen; einege Boost-Lizenz ähnlichBSD, MIT und GPL

ICU: C-C++-Java-Library für Unicode-Verarbeitung

7. Die Beziehungskiste von C und C++

C++ hat prinzipiell das komplette C von 1988 übernommen. Da es ein besseres C sein sollte, wurde dieses Prinzipdurchbrochen. Noch schlimmer: seit 1990 entwickelten sich C und C++ in verschiedene Richtungen. Seit 1999 arbei-ten die Standardisierungskomitees beider Sprachen zusammen und versuchen die entstandenen Inkompatibilitätenzu minimieren. Trotzdem soll sich C nicht zu einem zweiten C++ entwickeln; Zitat aus dem "Rationale for Inter-national Standard Programming Languages C Revision 5.10 April-2003": The Committee is content to let C++be the big and ambitious language. While some features of C++ may well be embraced, it is not the Committee'sintention that C become C++.

C und C++ sind also zwei verschiedene Sprachen, die man auseinanderhalten muss. Der gröÿere Teil von C kanntrotzdem mit gleicher Syntax und Semantik fast identisch in C und C++ geschrieben werden.

Page 6: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

6 1. EINFÜHRUNG IN C++

Die eigentlich notwendige, sorgfältige Di�erenzierung der beiden Sprachen wird oft verwischt (Microsoft, NVidia,qd-package) oder ist für den Programmierer unsichtbar, weil die Compiler �exibel umschalten (gcc).

Nicht jeder C-Compiler ist ein C++-Compiler (lcc), aber jeder C++-Compiler kann C übersetzen (gcc, clang,icc, pg). Nicht jeder C++-Compiler, der C übersetzt, macht das für die gesamte Sprache C und korrekt nachC-Standard (Nvidia, Microsoft).

7.1. C und C++ sind trotz verschiedener Syntax gleich. Die folgende Tabelle enthält Sprachkonstrukte,die absolut identisch wirken, auch wenn sie anders geschrieben werden müssen.

Tabelle 1. Syntaxunterschiede

C C++Jede C lib ist C++-lib #include<stdlib.h> #include<cstdlib>

Lib-Objekte sind im rand (); std::rand();

Namespace stdrand (); using namespace std; rand();

main ohne arg int main (void) int main ()

Hello World #include <stdio.h> #include <iostream>

int main (void) int main ()

{ printf ("Hello"); { std::cout << "Hello";}

return 0; }

Logik _Bool bool

Komplexe Zahlen float _Complex complex<float>

Konstante const int * p; int const * p;

struct typedef struct x {} name; struct name {};

struct typedef struct x {} name; class name {};

enum enum name {}; enum name x; enum name {}; name x;

union union name {}; union name x; union name {}; name x;

cast (int)x static_cast<int>(x)

dynamic memory malloc realloc free new deleteauto result int add(int a, int b) auto add(int a, int b) -> int

auto result template <type T, type U>

auto add(T t, U u) -> decltype(t+u)

{return a+b;}

7.2. C und C++ sind trotz gleicher Syntax verschieden. Die folgende Tabelle enthält Sprachkonstrukte,die in C++ weiter funktionieren, aber dort nicht mehr verwendet werden sollten, weil sie durch ein C++-eigenesKonstrukt ersetzt worden sind. Oft sind die C-Konstrukte in C++ deprecated (veraltet; von der Benutzung wirdabgeraten) und werden nur noch mit Warnungen übersetzt.

7.3. C++ hat wichtige Verbesserungen. Die folgende Tabelle enthält Sprachkonstrukte, denen in C nurein magerer Ersatz entspricht.

7.4. Beide Sprachen enthalten auch Unsinn. Ohne drastischere Worte verwenden zu wollen, �nden sichhier Beispiele, die man weder in C noch in C++ bei guten Programmierern jemals �nden wird.

Und ja, ich halte die schleifenlokalen Variablen in den meisten Fällen für eine schlechte Wahl und ein Hindernisbei der Programmentwicklung und -p�ege.

7.5. Mängel von C++. Schlieÿlich kann man für C++ viele Mängellisten aufstellen. Es gibt teilweise heftigeDiskussionen, ob solche Einträge bugs oder features sind, ob sie vielleicht sogar positive Eigenschaften sind. Einekleinere für den Anfänger:

• kein garbage collector• Das Wort unde�ned taucht im Standard viel zu oft auf• Name (de-)mangling ist nicht standardisiert (und: vernünftige Sprachen brauchen es gar nicht)

Page 7: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

7. DIE BEZIEHUNGSKISTE VON C UND C++ 7

Tabelle 2. Semantikunterschiede

C C++ deprecated C++iostream statt stdio #include<stdio.h> #include<cstdio> #include<iostream>

cin/cout/cerr/clog printf ("Hallo"); std::printf ("Hallo"); std::cout << "Hallo";

statt printfstring statt cstring #include<string.h> #include<cstring> #include<string>

string statt char* char * s = "Hallo"; char * s = "Hallo"; string s = "Hallo";

Container statt Felder double v [100]; vector<double> v;

auto statt Typen _Bool b = true; bool b = true; auto b = true; // bool

templates statt Typen double _Complex complex<double>

Referenzen statt Zeiger void f (char * s) void f (string & s)

inline ist anders immer explizit in Klassen implizitstattFehlerende exit(1) exit(1) main-function-try-block

catch-return-1

Tabelle 3. C++, a better C

C C++namespaces Namensprä�xe namespacesEllipse, vararg void f (...) overloading, default argumentsMacros #define x(a) sqrt (a++) inlineexceptions exceptionsexceptions exceptions

Tabelle 4. Unsinn

C C++trigraphs ??< ??<

bigraphs <: <:

getsstrtokDu�s devicefor (int i = 0. . .

• Zeiger• Der Präprozessor• keine Feldgrenzenüberwachung (segmentation faults)• C++-Programme sind schwerer lesbar als C und oft schwerer verständlichstd::vector<int> v; auto n = std::count(begin(v), end(v)); verbessert die Lesbarkeit seit 2011

• Schwierige Fehlersuche; Compilermeldungen sind schwer verstehbar• Schwierige Fehlersuche; Laufzeitmeldungen sind schwer verstehbar• kein finally

• Unicode support gewöhnungsbedürftig• Der Quellcode in .h muss in groÿen Projekten immer wieder übersetzt werden• Code bloat• Übersetzungszeiten• T t; ist Konstruktoraufruf, T t(); ist Funktionsdeklaration und T t(1); ist wieder Konstruktoraufruf.• char * p; p = nullptr; cout << *p; erzeugt keine Exception sondern segmentation fault.• string und complex sind in der Bibliothek de�niert und damit langsamer und ohne Compiler-Optimierung.• Konzeptlos aus Einzelteilen zusammengeschusterte Sprachteile, jedes gut, aber im Zusammenspiel schlecht

(overloading, default args, templates).• Raw-Strings sind ein reiner Murks und führen zu ähnlich gut lesbaren C++-Programmen wie Perl.

Page 8: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

8 1. EINFÜHRUNG IN C++

8. Ein erster Eindruck

itor0.c ist ein C-Programm, dass die in einem String vorkommenden Schriftzeichen ermittelt. Es kann auch mitC++ übersetzt werden.

itor0/1/3/4/5 demonstrieren die sehr verschiedenen Sprachdialekte von C++.

itor0 benutzt praktisch keine C++-Fähigkeiten.

itor1 enthält C-Libraries, namespace std, main ohne void, C++-Ausgabe, new und delete.

itor3 verwendet C++-Mengen (set) zur Ermittling des Ergebnisses und Algorithmen (foreach) zur Ausgabe. Frap-pierend ist das Verschwinden der eigentlichen Arbeit in den Deklarationen.

itor4 demonstriert λ-Funktionen aus C++14.

itor5 ermittelt nicht die Schriftzeichen, sondern die Wörter mit regular expressions aus C++14.

Schon hier sieht man einen Nachteil von C++: die längeren Übersetzungszeiten. Bei diesem Programm erkenntman in der Laufzeit praktisch keine Unterschiede, weder zwischen den Compilern, noch zwischen den doch sehr ver-schiedenen Methoden. (Die Arbeit muss halt in jedem Fall irgendwie gemacht werden!) Nicht eimnal die ine�zienteProgrammierung der Sortierung in itor0 und itor1 ist erkennbar.

Die erste Zahl ist die Compile-Zeit, die zweite die Laufzeit des Programms. Eingetragen wurde der Median vonjeweils drei Messungen. Alle Übersetzungen sind optimiert. Die Messung wurde mit Strings der Länge 10000durchgeführt. Der Test lief 2015 auf der Athene (Quad-Core AMD Opteron Processor 2354)

Tabelle 5. Rechenzeiten

Programm Gnu gcc LLVM IntelC -o2 itor0.c 0.132/34.466 0.125/34.449 0.343/34.848C++ -o2 itor0.c 0.194/34.486 0.184/34.450 0.416/34.471C++ -o2 itor1.cpp 0.397/34.477 0.391/34.470 0.585/34.471C++ -o2 itor3.cpp 0.605/34.080 0.493/34.487 0.769/34.464C++ -o2 -std=C++11 itor4.cpp 0.964/34.438 1.174/34.551C++ itor5

Page 9: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

KAPITEL 2

Prozedurale Programmierung

Paradigmen: Maschinenorientiert (Assembler), Formelorientiert (FORTRAN, mathematische Formeln), Prozedural(Algol-Familie), Funktional (LISP, λ-Kalkül von Church), Deskriptiv (Prolog, Prädikatenlogik), Objektorientiert(Smalltalk, Ei�el, C++, bezogen auf die Datenobjekte), Generisch (Ada, C++)

1. Struktur von C++-Programmen

1.1. main. Jedes C++-Programm muÿ genau eine globale Funktion mit dem Namen main enthalten, mitdem die Programmausführung beginnt.

Zwei Formen eines C++-Hauptprogramms sind im Standard de�niert; weitere Formen sind implementierungsab-hängig und sollten für portable Programme vermieden werden. Da argumentlose Funktionen in C++ ohne das Wortvoid deklariert werden, fehlt void selbstverständlich auch bei main. In main darf die return-Anweisung entfallen,wenn sie eine 0 erzeugt. Ein C++-Programm lautet daher:

1 // main1.cpp

2 int main () { }

Werden die Programmparameter argc und argv vom Betriebssystem übernommen, schreibt man wie in C:

1 // main2.cpp

2 int main (int argc , char * argv []) { }

3 // alternativ:

4 //int main (int argc , char ** argv) { }

Die Argumente argc und argv sind wie in C de�niert. (argc≥ 0, argv [0] . . . argv [argc-1], argv [argc] ≡ 0,argv [i] ist NTMBS � null terminated multibyte string). Da die Argumente besser als C++-Strings weiterverar-beitet werden, folgt ein kleines Rezept zur Konversion (es sollte eigentlich in der STL sein!):

1 // main2a.cpp

2 #include <iostream >

3 #include <vector >

4 using namespace std;

5 int main (int argc , char * argv [])

6 {

7 for (int i = 0; i < argc; i++) cout << argv [i] << endl;

8 vector <string > args (argc);

9 for (int i = 0; i < argc; i++) args [i] = string (argv [i]);

10 for (int i = 0; i < argc; i++) cout << args [i] << endl;

11 }

Folgen implementierungsabhängige weitere Parameter von main, so müssen sie nach den Standardparametern ste-hen. Beliebt, aber nicht standardisiert, und deshalb zu vermeiden, ist hier char ** env.

Jede Funktion, auch main, darf als Rumpf einen function-try-block haben:1 // maintry.cpp

2 #include <exception >

3 int main (int argc , char** argv)

4 try

5 {

6 return 0;

7 }

8 catch (std:: exception e)

9 {

9

Page 10: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

10 2. PROZEDURALE PROGRAMMIERUNG

10 return 1;

11 }

Der Ergebnistyp von main ist implementierungsabhängig, jede Implementierung muÿ jedoch int zulassen. Dasint-Ergebnis wird von vielen Betriebssystemen als Information über das Programmende interpretiert; 0 ist dannimmer fehlerfreies Programmende, Werte > 0 sind programmde�nierte Fehlernummern bei unvorhergesehenenProgrammstops.

main ist nicht überladbar und nicht rekursiv aufrufbar. Die Adresse von main kann nicht ermittelt werden. maindarf nicht inline oder static sein. Der Name main ist trotzdem nicht reserviert und darf als Bezeichnung fürandere Gröÿen (z.B. Memberfunktionen, Klassen, Aufzählungen, . . . ) verwendet werden, wenn keine Verwechslungmit dem Hauptprogramm möglich ist.

Die programmbeendende Anweisung return 0; darf in C++ entfallen. Sie wirkt wie in C, d.h. ein Programm kannaus main heraus mit der Anweisung return e;beendet werden. Dabei ist e die dem Betriebssystem übergebeneFehlernummer. Steht die Anweisung return e; nicht explizit im Hauptprogramm, so wird das Programm mit derFehlernummer 0 beendet.

Wie in C darf in C++ an jeder Stelle eines Programms mit der in <cstdlib> deklarierten Funktion exit (e); einProgramm beendet werden. Das sollte jedoch immer vermieden werden, da der Compiler bei exit() wie bei jederFunktion annimmt, dass sie nach ihrem Ende zurückkehrt und dass das Programm danach normal endet. Dadurchentfällt die eigentlich notwendige Blockabschluÿarbeit (Destruktoren. . . ). Das ist im Grunde ein Programmierfehler,so dass exit() in C++ eigentlich nichts verloren hat! Die korrekte Lösung ist ein function-try-block mit return 1;

im catch.

Wer exit() trotzdem verwenden will, sollte dem Compiler anzeigen, dass die exit() aufrufende Funktion nichtzurückkehrt:

1 // mainexit.cpp

2 #include <iostream >

3 #include <cstdlib >

4

5 [[ noreturn ]]

6 int error (int i)

7 {

8 std::cout << std::endl << "Fehler " << i << std::endl;

9 std::exit (1);

10 }

11

12 int main (int argc , char * argv [])

13 {

14 if (argc < 3) error (2);

15 std::cout << "doing whatever now" << std::endl;

16 }

1.2. Kommentare. C++ kennt dieselben Kommentarformen wie C99: nicht verschachtelbare, mit /* ... */

geklammerte Kommentare haben die Form/* ... /* ... * ... */

Weiter gibt es Zeilenendkommentare, die mit // eingeleitet werden und dem nächsten Zeilenende enden. Sie habendie Form:

// ...

Auch in C99 wurde diese Kommentarart übernommen. C++ und C99 auf der einen Seite und C89 auf der anderenführen die folgende Anweisung unterschiedlich aus:

x = a//* Kommentar */ b

+ c;

In C89 wird x = a/b + c; berechnet, während in C++ und C99 die Division im Kommentar verschwindet.x = a + c;

Page 11: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

1. STRUKTUR VON C++-PROGRAMMEN 11

1.3. Präprozessor. Der Präprozessor ist wie in C de�niert und funktioniert unverändert weiter.

Es gibt einen Unterschied in den Include-Anweisungen der Standardbibliotheken in C: Die veraltete C-Form(stdlib.h) bringt die Deklarationen ohne Namespace ein; die C++-Form (cstdlib) von 1999 erzeugt die Dekla-rationen im Namespace std und erfordert die explizite Namespaceangabe (using namespace std;).

Wird mit einem C++-Compiler übersetzt, dann ist das Präprozessorsymbol __cplusplus (2 Unterstriche!) mitden Werten 1, 199711L, 201103L oder 201402L de�niert. Es kann mit der Präprozessoranweisung

#ifdef __cplusplus

...

#endif

im Programm abgefragt werden.

Weitere Präprozessorsymbole sind

__STDC_HOSTED__, wenn das Programm unter einem Betriebssystem arbeitet und kompletten Zugang zu allen Bi-bliotheken hat (Gegenteil: freestanding)__STDC_MB_MIGHT_NEQ_WC__ char und wchar_t Codes können beim selben Zeichen verschiedene Werte haben__STDC__ISO_10646 yyyymmL, Unicode required wchar_t-Zeichen des angegebenen Datums haben alle den rich-tigen Code; Beispiel 199712L__STDCPP_STRICT_POINTER_SAFETY__ genau das (ISO/IEC 14882 3.7.4.3); gedacht für in C++ inexistente garbagecollection; Gegenteil: relaxed__STDCPP_THREADS__ mehr als 1 Thread erlaubt

__DATE__ Übersetzungsdatum__FILE__ Quelldatei__LINE__ Quellzeile__TIME__ Übersetzungsuhrzeit__STDC__ kann existieren und hat in C++ nichts verloren__STDC_VERSION__ kann existieren und hat in C++ nichts verloren

1.4. Bestandteile von C++, Zeichen und tokens. Der Begri� token beschreibt in Programmiersprachendie Elemente, aus denen sich der Programmtext zusammensetzt. Für C++ werden sie in diesem Kapitel beschrieben.Es sind Operatorsymbole, Namen und Literale. Für Anfänger ist dieses Kapitel ein Nachschlageteil und keinStartpunkt zum Erlernen der Sprache.

1.4.1. Schriftzeichen und Operatoren. C++-Programme werden mit einer Schriftzeichenmenge (Zeichensatz)mit der Bezeichnung source character set geschrieben. Nach der Übersetzung verarbeitet das laufende ProgrammSchriftzeichen einer zweiten Menge, der execution character set. In beiden Fällen muss eine Minimalmenge vorliegen,die basic character set, die durch eine extended character set erweitert sein kann.

Die Minimalmenge besteht aus den 52 Buchstaben des englischen Alphabets, den zehn Zi�ern, 29 Sonderzeichenund fünf nichtsichtbaren Gliederungszeichen. Bei der Ausführung kommen drei weitere Zeichen zur Textgestaltungdazu:

Buchstaben ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz

Zi�ern 0123456789

Sonderzeichen _ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " '

Gestaltungszeichen: \t\v\f\n und zusätzlich \a\b\r

Mit diesen Schreibweisen sind das Leerzeichen ( ), der Tabulator (\t), der Vertikaltabulator (\v), der Seitenvor-schub (\f) und der Zeilenvorschub (\n) gemeint. Die Zusatzzeichen bei der Ausführung sind der Alarm, (\a) dieRücktaste (\b) und der Wagenrücklauf (\r).

Der Zeilenvorschub \n muss nicht durch ein Zeichen dargestellt werden.

Mit sehr tastenarmen Tastaturen kann man neun dieser Zeichen durch Trigraphs ersetzen, die immer mit einem doppeltenFragezeichen beginnen:

Page 12: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

12 2. PROZEDURALE PROGRAMMIERUNG

# ??= ] ??) | ??![ ??( ^ ??' } ??>

\ ??/ { ??< ~ ??-

Ähnlich können 17 Zeichen durch Digraphs oder Worte ersetzt werden:

[ <: ] :> { <% } %> # %: ## %:%:

&& and | bitor || or ^ xor ~ compl & bitand

&= and_eq |= or_eq ^= xor_eq ! not != not_eq } %>

} %> } %> } %> } %>

Ich habe noch kein Programm gesehen, das statt {int x = a[b]^c|~d; } die erlaubte (!) Schreibweise<%int x = a??(b:>??'c??!??-d; %> verwendet.

Für manche mathematischen Zeichen existiert keine Taste; sie wird durch eine Kombination dargestellt:

≤ <= ≥ >= ≡ == ̸= =!∧ && ∨ || ¬ ! → ->

Ohne mathematisches Äquivalent wurden die folgenden Kombinationen für C++ erfunden:

++ -- << >> += -= *= /=

%= <<= >>= &= ^= |= ##

Zuletzt werden die Interpunktionszeichen in noch einmal als Gruppe zusammengefaÿt:

[ ] ( ) { } . -> ++ --

& * + - ~ ! / % << >>

< > <= >= == != ^ | && ||

? : ; ... = *= /= %= += -=

<<= >>= &= ^= |= , # ## <: :>

<% %> %: %:%:

1.4.2. Namen ( identi�er). Namen zur Bezeichnung von Programmeinheiten beginnen mit einem Buchstabenoder einem Unterstrich. Danach folgen weitere Buchstaben, Unterstriche oder Zi�ern. Ein Unterstrich am Anfangist für Spracherweiterungen reserviert.

C und C++ erlauben schon lange Unicode-Buchstaben in Namen. Die meisten Compiler verstehen das jedochnicht. Auch wegen der internationalen Lesbarkeit sollte man tschechische oder chinesische Variablennamen ehervermeiden.

int i\u00c4\u5c71; // Ä und shan (chin.)

Solche Programme sind nur mit Zusatzoptionen übersetzbar:

g++ -fextended-identifiers -finput-charset=utf8 x.cpp

C++ ist case-sensitiv und formatfrei.

Die folgenden Namen sind Schlüsselwörter und dürfen nur in ihrer festegelegten Bedeutung verwendet werden:

alignas alignof asm auto bool break case catch char char16_t char32_t class const const_cast constexpr continuedecltype default delete do double dynamic_cast else enum explicit export extern false �oat for friend goto if inlineint long mutable namespace new noexcept nullptr operator private protected public register reinterpret_cast returnshort signed sizeof static static_assert static_cast struct switch template this thread_local throw true try typedeftypeid typename union unsigned using virtual void volatile wchar_t while

1.4.3. Literale.

Page 13: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

6. FUNKTIONEN 13

2. Hello World

Durch mehrere Möglichkeiten, den namespace std anzugeben, existieren zwei Varianten eines einfachen Hallo-Programms. Man kann jeden Namen mit std quali�zieren:

1 // hello1.cpp

2 #include <iostream >

3 int main()

4 {

5 std::cout << "Hello" << std::endl;

6 }

Alternativ wird der gesamte Namensraum std im Programm verfügbar gemacht:

1 // hello2.cpp

2 #include <iostream >

3 using namespace std;

4 int main()

5 {

6 cout << "Hello" << endl;

7 }

Verwenden Sie niemals die C-Library direkt stdio.h oder in der C++-Form cstdio mit z.B. printf! Ein-undAusgaben wird in C++ immer mit iostream durchgeführt. Den C-Geräten stdin, stdout und stderr entsprechenin C++ die Ströme cin, cout, cerr und clog. Die Operationen werden suggestiv mit den Stream-Insertern und-Extraktoren << und >> durchgeführt. Zeilenenden werden mit endl oder wie in C gewohnt mit "\n" geschrieben.

3. Datentypen, Variable und Werte

3.1. Typprüfung. Typprüfungen sind strenger als in C.

3.2.

3.3.

3.4.

4. Rechenausdrücke

5. Anweisungen

6. Funktionen

6.1. Überladung.

6.2. Default Argumente.

6.3. void/variable arguments.

6.4. C-Funktionen.

Page 14: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

14 2. PROZEDURALE PROGRAMMIERUNG

7. Header

8. Namespaces

Namespaces sollen in groÿen Programmen, die manchmal aus Teilen verschiedener Herkunft zusammengesetztsind, Namenskon�ikte vermeiden. Der Klassiker ist eine in C und C++ fehlende ganzzahlige Potenz, die oft adhoc in verschiedenen Programmteilen unter dem Namen ipow eingeführt wird. Die Kollision entsteht, wenn beideProgrammteile gemeinsam übersetzt werden und beide Funktionen vielleicht sogar leicht verschieden de�nierteLeistungen erbringen.

Namespace-Blöcke werden mit einem Namespace-Namen versehen, ihr Inhalt wird zusätzlich mit dem Namespace-Namen quali�ziert.

namespace name { De�nitionen und Deklarationen wie int i; }

Auÿerhalb des Namespace heiÿt die Variable name::i. So können verschiedene gleichnamige Entities durch ver-schiedene Namespace-Namen unterschieden werden.

Mehrere Namespace-Blöcke mit demselben Namespace sind erlaubt und werden als gemeinsamer Namespace be-handelt. Solche Blöcke dürfen sich sogar über verschiedene Übersetzungseinheiten erstrecken.

Namespaces dürfen beliebig tief verschachtelt sein, also innere Namespaces enthalten. Die Bezeichnungen folgendann den Verschachtelungen.

namespace n1 { int i; namespace n2 { int j; j = 1; } i = 2; n2::j = 3; }

n1::i = 4; n1::n2::j = 5;

Will man die Namespace-Entities ohne den namespace-namen benutzen, kann man das mit using-Direktive tun.

using namespace n1; //Alle Namen in n1 dürfen mit und ohne Namespace-Angabe benutzt werden: n1::i = 5; i = 1;

Mit der using-Deklaration kann man einzelne Entities ohne Namespace-Angabe benutzen.

using n1::i; // Der Name i darf mit und ohne Namespace-Angabe benutzt werden: n1::i = 5; i = 1;

Ein mit inline markierter Namespace überträgt seinen Inhalt in den umgebenden Namespace. Dabei dürfen keineNamenskollisionen auftreten. Verschachtelte inlines sind erlaubt.

namespace n1 { int i; inline namespace n2 { int j; j = 1; } i = 2; n2::j = 3; j = 4; }

n1::i = 5; n1::n2::j = 6; n1::j = 6;

Namespaces ohne Namen sind erlaubt und generieren einen mit unique bezeichneten Namespace mit impliziterusing-Direktive mit der storage-Klasse internal linkage. Die Namen sind also nur in einer Übersetzungseinheitsichtbar.

namespace { int i; i = 1; } i = 2; unique::i=3;

Namespaces dürfen in einzelnen Programmteilen umbenannt werden. Ihre Entities können dann unter einem anderenNames (alias-name) benutzt werden.

using zweitname=namespacename;

Schlieÿlich werden ab 2017 für verschachtelte Namespaces Kurzschreibweisen erlaubt.

namespace A::B::C { } ersetzt namespace A { namespace B { namespace C { }}}

9. Referenzen

10. Schriftzeichen, Strings und Unicode

10.1. Schriftzeichen. C++ hat vier Schreibweisen für Schriftzeichen, die sich im Typ unterscheiden:'x' (narrow literal), Typ: charu'x' Typ: char16_tU'x' Typ: char32_tL'x' Typ: wchar_t

Page 15: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

10. SCHRIFTZEICHEN, STRINGS UND UNICODE 15

Die Schriftzeichen selbst dürfen aus der source character set sein. Sie wird vom Compiler implementierungsabhängigauf die execution character set abgebildet, falls Schriftzeichen verwendet werden, die nicht aus der Grundzeichen-menge stammen (basic source character set, 96 Zeichen: a-z A-Z 0-9 _{}[]()#<>%:;.? *+-/^&|~!=,\"').

Wie in C sind Escape-Schreibweisen erlaubt: \' \" \? \\ \a \b \f \n \t \v. Auch die oktalen und sedezimalenEscapes A ≡ \101 ≡\x41 sind problemlos. Der in C sehr häu�ge Stringabschluss \0 wird verstanden, wurde inC++ natürlich durch echte Strings ersetzt.

Unicode-Zeichen dürfen (auch im normalen Programm) mit zwei unicode-Escapes geschrieben werden: \uxxxxund \Uxxxxxxxx. Hier sind ausschlieÿlich sedezimale vier- oder achtstellige code-points aus dem Unicode erlaubt,surrogate-Schreibweisen sind verboten; verwenden Sie statt dessen die achtstellige Variante mit groÿem U.

Mehr als ein Schriftzeichen ist erlaubt, wird als int ausgewertet und der Wert ist implementierungsabhängig(multicharacter literal, 'ab', u'2e'). Zwischen Apostrophen sollte in portablen Programmen niemals mehr als einSchriftzeichen stehen. Wegen der Probleme gibt gcc eine Warnung aus.

Anders als in C sind char, char16_t, char32_t und wchar_t in C++ Schlüsselworte.

Faustregel: Wenn Sie nur ASCII verwenden, machen Sie weiter wie bisher! Wenn Sie andere Zeichen brauchen(ÄÖÜäöüÿÑñéèe. . . ), benutzen Sie konsequent UTF-8-Strings, die Unicode-Escape-Schreibweisen und die C++-Stringlibrary. UTF-16 ist ebenfalls brauchbar. Wenn Sie die Strings im Programm verarbeiten wollen, benutzen Sieeine gute Unicode-Library (ICU)!

10.2. Strings. Für Strings stehen fünf Grundschreibweisen in zwei Varianten zur Verfügung:

"Hallo" Inhalt: ASCII, ISO-8859-1, CP1252 oder what you will1

u8"Hallo" Inhalt: UTF-8 in char

u"Hallo" Inhalt: UTF-16 in char16_t

U"Hallo" Inhalt: UTF-32 in char32_t

L"Hallo" Inhalt: compiler-de�ned in wchar_t

Mit Ausnahme der 96 Zeichen der basic source character set ist die Abbildung der source character set auf die exe-cution character set implementierungsabhängig, also ist nicht eindeutig klar, was ein Compiler mit L"2e" wirklichanfängt. Besser ist in jedem Fall u8"2\u20AC" oder U"2\U000020AC". Ob der Compiler oder das Betriebssystembei der Programmausführung mit den Strings klarkommen, ist eine weitere spannende Frage.

Überraschenderweise erzeugt <iostream> bei der Ausgabe nur bei ASCII- und UTF-8-Strings eine Zeichenfolge,bei allen anderen Stringvarianten eine Adresse.

Alle Strings können auch als Raw-Strings geschrieben werden, diese Form erlaubt keine Escapes und jedes Zeichenaus der source character set wird eins zu eins in den String übernommen. Zwischen Pre�x und String steht dannnoch ein R:

u8R"abc( 2 efür meinen Kommentar\n

)abc"

steht für u8" 2 \u20AC\nfür meinen Kommentar\n\\n\n". Gänsefüÿchen, abc und die Klammern begrenzen denString. Der Text abc als Begrenzer ist frei wählbar und darf auch fehlen.

10.3. STL-Strings. Die STL-Library bietet eine ausgefeilte, jedoch noch an klassischem C orientierte String-Library in <string>. Sie setzt fälschlicherweise Schriftzeichen ≡ code unit voraus.

Das folgende Beispiel demonstriert die Leistungen und Grenzen:

1 // str1.cpp

2 #include <iostream >

3 #include <string >

4 #include <algorithm >

5

1Shakespeare: Thwelfth Night

Page 16: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

16 2. PROZEDURALE PROGRAMMIERUNG

6 using namespace std;

7

8 int main ()

9 { string s, stmp;

10 int i, j;

11

12 getline (cin , s);

13 stmp = s;

14 cout << stmp << endl;

15

16 cout << "backwards: ";

17 for (i = s.length () - 1; i >= 0; --i) // first solution

18 { cout << s [i];

19 }

20 cout << endl;

21

22 s = stmp;

23 for (j = 0, i = s.length () - 1; j < i; ++j, --i) // K&R

24 { char tmp = s [i];

25 s [i] = s [j];

26 s [j] = tmp;

27 }

28 cout << "swap pairs: " << s << endl;

29

30 s = stmp;

31 reverse (s.begin(), s.end()); // using algorithm

32 cout << "reverse: " << s << endl;

33

34 s = string(stmp.rbegin (), stmp.rend()); // reverse iterator

35 cout << "rev. it.: " << s << endl;

36

37 stmp = string(u8"1 $ = 0.8752 \u00e2\u201a\u00ac = 0.6923 \u00c2\u00a3 \u00e2\u02dc\u00ba \

u00f0\u0178\u02dc\u201e"); // UTF -8 reverse

38 cout << stmp << endl;

39 cout << stmp.length () << endl;

40 s = string(stmp.rbegin (), stmp.rend()); // reverse iterator

41 // auto abbreviates

42 /*

43 std:: string :: iterator ib , ie;

44 ib = s.begin ();

45 ie = s.end();

46 */

47 auto ib = s.begin ();

48 auto ie = s.end();

49 while (ib != ie)

50 {

51 if (((*ib) & 0xc0) == 0x80) // revert multi -unit -chars

52 { // a second time

53 auto i = ib;

54 while (i != ie && (((*i) & 0xc0) == 0x80)) i++;

55 i++; // i = end of muc

56 reverse (ib, i);

57 ib = i;

58 }

59 else

60 {

61 ib++;

62 }

63 }

64 cout << "UTF -8 rev.: " << s << endl;

65 }

Page 17: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

10. SCHRIFTZEICHEN, STRINGS UND UNICODE 17

10.3.1. Grundprinzip. Gilt für string und auch für vector: Mehrere Typen scharf unterschieden: 1. string un-verändert (size()); 2. bestehender string-Bereich manipuliert (s[0]='A'); 3. String erweitert ();

Eine bereichsmanipulierende Funktion kann nicht erweitern (s[10000000]='A';)

10.3.2. Variable und Initialisierung. string s, s1(s), s2("Hallo"), s3(10, 'A'), s4(s2,1,3), s5(s2.begin(),s2.end());

10.3.3. Iteratoren.

10.3.4. Operatoren. s1=s2; s[i]; s+s1; s==s1; s<s1;

10.3.5. Funktionen. s1.assign(s2);

10.3.6.

10.4. C++ und Unicode. C++ hat C komplett übernommen (char); beide Sprachen haben zweimal ver-sucht, den Unicode mühsam zu integrieren: einmal ca. 1999 (wchar_t, wprint, "%ls", towupper) und einmal2011 (char16_t, char32_t). Beide Sprachen sind hier nicht kompatibel: in C muss bei wchar_t-Verwendung<stdlib.h> inkludiert werden, bei char16_t- oder char32_t-Verwendung jedoch <uchar.h>. In C++ sind alledrei Typen Sprachschlüsselworte!

Auch die Leistungen von wchar_t (C++98) und char16_t/char32_t (C++11) sind nicht äquivalent.

Der Unicode de�niert den Code und mehrere encodings, macht aber keine Vorgaben, welche encodings verwendetwerden sollten. Er sagt nichts aus über die Schnittstellen zwischen Tastatur, Betriebssystem und Programm, undes gibt keinerlei Aussagen über die Darstellung der Zeichen auf Papier oder Bildschirm.

In Java und JavaScript ist für die interne Verarbeitung als code-unit eine Zwei-Byte-Einheit mit encoding UTF-16festgelegt, in Javascript engineabhängig auch nur UCS-2. Das encoding-scheme (endianess) steht der CPU frei.

C und C++ enthalten keine Vorgaben über encodings. Diese Entscheidung muss prinzipiell der Programmierertre�en; er sollte jedoch möglicherweise Randbedingungen berücksichtigen. Die meisten encodings (UTF-1, UTF-4,UTF-5, UTF-7, SCSU, BOCU) können heute erfreulicherweise ignoriert werden; wichtig sind eigentlich nur nochASCII, UTF-8, UTF-16 und UTF-32. Das encoding-scheme regelt auch hier die CPU.

Randbedingungen sind die Inkompatibilität zwischen Microsoft-Compilern und Windows-Rechnern auf der einenSeite (sizeof(wchar_t)==2 und UTF-16) und Gnu-C, Intel-C und LLVM-C auf der anderen (sizeof(wchar_t)==4und UTF-32). wchar_t ist nicht portabel und sollte vermieden werden!

char16_t und char32_t sollten portabel sein, werden jedoch nur von aktuellen Compilern beherrscht (gcc -std=c11,g++ -std=c++11, g++ -std=c++14).

Damit zeichnen sich folgende brauchbare Empfehlungen ab:

1. Eine gute Wahl ist die Verwendung von char und UTF-8 als interne Darstellung.

2. Eine Alternative ist char16_t und UTF-16 oder char32_t und UTF-32. Hier ist einschränkend zu beachten,dass <iostream> nur Adressen ausgibt (cout<<u"Hallo";).

3. Wer wirklich vielsprachige Texte verarbeiten muss, kommt um die Library ICU von IBM nicht herum. Siehat einen eigenen Datentyp für Schriftzeichen UChar (intern UCHAR_TYPE) und verwendet UTF-16, kann aber alleencodings konvertieren.

Lesen und Schreiben von Dateien setzt für eine Datei voraus, dass Sie ihr encoding (UTF-8. . . ) und ihr encoding-scheme (big/little endian) kennen. Interne Strings und Datei-Format müssen eventuell konvertiert werden. Auchhier leistet ICU gute Dienste.

Ausgaben auf den Bildschirm oder Drucker benötigen installierte Fonts zur Darstellung der Glyphen.

Eingaben von der Tastatur gehen nur, wenn die Tastatur Vielsprachigkeit ermöglicht, das Betriebssystem die Zei-chen überträgt und das Programm das alles richtig versteht. In Unices sind die Locale-Einstellung hilfreich:setlocale(LC_ALL, "Russian");

_wsetlocale(LC_ALL, L"Russian");

Wichtigste Regel für alle Programmierer:

char ist kein Schriftzeichen mehr, sondern nur noch code-unit. Ein oder mehrere code-units können ein Schriftzeichensein, das hängt von encoding und vom Schriftzeichen selbst ab.

Page 18: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

18 2. PROZEDURALE PROGRAMMIERUNG

wchar_t oder char16_t ist ebenfalls kein Schriftzeichen, sondern nur code-unit. Ein oder zwei code-units könnenein Schriftzeichen sein, das hängt von encoding und vom Schriftzeichen selbst ab.

Nur mit char32_t fallen code-units und Schriftzeichen wieder zusammen. Dafür werden alle Schriftzeichen in vierBytes gespeichert.

Die in den Sprachstandards de�nierten Libraries verarbeiten keine Schriftzeichen mehr, auch wenn ihre Namen dassuggerieren. Sie verarbeiten lediglich code-units! std::string s = u8"2e"; s.size() liefert nicht die Anzahl derSchriftzeichen (2), sondern die Anzahl der code-units (1+3=4). Insbesondere gilt das für <cstring> und <string>,hier für string, wstring und u16string. Für u32string sind Zeichen und Codeunits wieder identisch.

Microsoft benutzt in Windows für den Unicode wchar_tmit UTF-16. Nicht portabel ist gcc mit wchar_t = UTF-32.

10.5. Übersicht. Die folgende Tabelle gibt eine Übersicht über die De�nitionen im Standard:

char string

wchar_t wstring

char16_t u16string

char32_t u32string

Null-terminated sequence <cctype>

Null-terminated sequence <cwctype>

Null-terminated sequence <cstring>

Null-terminated sequence <cwchar>

Null-terminated sequence <cuchar>

10.6. Beispiel. Im folgenden Beispiel werden Funktionen vorgestellt, die mit Sprachmitteln die verschiede-nen Stringvarianten auÿer wchar_t konvertieren. Mit dem optional übersetzbaren main können sie gleich getestetwerden.

Die seit C++11 vorhandene Library codecvt (im C++14-Standard unter 22.3.3.2 beschrieben) enthält ähnlicheLeistungen; sie ist jedoch vorläu�g ohne Ersatz ab C++17 deprecated. O�zielle Empfehlung: selbst implementieren!

Die Funktionen sind so kurz wie möglich implementiert und arbeiten korrekt für unicode-korrekte Eingabestrings.Sie prüfen nicht alle im Unicode verbotenen Darstellungen. Bei nicht korrekten Darstellungen liefern sie unde�-nierte Ergebnisse (UTF-8-Zeichen, die als Fortsetzung starten, halbe UTF-16-Paare, verbotene Zeichen). SpezielleUnicodeprobleme wie combining characters werden hier nicht bearbeitet.

Man kann die Bearbeitung von UTF-8- und UTF-16-Strings gut erkennen und nachvollziehen.

1 // uconvert.cpp

2 // g++ -std=c++11 -Dtestmain uconvert.cpp

3 // g++ -std=c++11 -c uconvert.cpp

4

5 #include <iostream >

6 #include <fstream >

7 #include <string >

8 #include <locale >

9 #include <iomanip >

10 //#include <codecvt > introduced 2011, deprecated 2017

11 #include <vector >

12

13 #include "uconvert.h"

14

15 std:: string toUtf8 (std:: u16string s) // u16 -> u32 -> u8

16 { std:: u32string t = toUtf32 (s);

17 return toUtf8 (t);

18 }

19

20 std:: u16string toUtf16 (std:: string s) // u8 -> u32 -> u16

21 { std:: u32string t = toUtf32 (s);

22 return toUtf16 (t);

23 }

24

25 // recursivly generate UTF -8 bytes from c to v

Page 19: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

10. SCHRIFTZEICHEN, STRINGS UND UNICODE 19

26 // 10000000=n (marks 0 bit in 1st byte) bits n ~(2n-1)

27 // 0xxxxxxx 7 bit 0x80 0x00

28 // 110 xxxxx 10 xxxxxx 11 bit 0x20 0xc0

29 // 1110 xxxx 10 xxxxxx 10 xxxxxx 16 bit 0x10 0xe0

30 // 11110 xxx 10 xxxxxx 10 xxxxxx 10 xxxxxx 21 bit 0x08 0xf0

31 void u8enter (std::vector <char > & v, int c, int n)

32 { int m;

33 if (c < n) // rest of char in c fits into first byte

34 { m = ((~(2*n-1)) & 0xff) | c;

35 v.push_back (static_cast <char >(m));

36 return;

37 }

38 u8enter (v, c >> 6, n==0x80 ? n>>2 : n>>1);

39 v.push_back (static_cast <char >((c & 0x3f) | 0x80));

40 }

41 std:: string toUtf8 (std:: u32string s) // u32 -> u8

42 { std::vector <char > v;

43 for (int i = 0; i < s.size(); ++i) u8enter (v, s [i], 0x80);

44 return std:: string (v.begin (), v.end());

45 }

46

47 void u32enter (std::vector <char32_t > & v, char32_t & c, int & n, int s)

48 { if ((s & 0x80) == 0x00) { v.push_back (s & 0x7f); c = U'\0'; n = 0; }

49 else if ((s & 0xe0) == 0xc0) { c = s & 0x1f; n = 1; } // 110 xxxxx

50 else if ((s & 0xf0) == 0xe0) { c = s & 0x0f; n = 2; } // 1110 xxxx

51 else if ((s & 0xf8) == 0xf0) { c = s & 0x07; n = 3; } // 11110 xxx

52 else if ((s & 0xc0) == 0x80) // 10 xxxxxx

53 { c = (c << 6) | (s & 0x3f);

54 n--;

55 if (n <= 0) { v.push_back (c); c = U'\0'; n = 0; }

56 } }

57 std:: u32string toUtf32 (std:: string s) // u8 -> u32

58 { std::vector <char32_t > v;

59 char32_t c;

60 int n;

61 c = U'\0'; n = 0;

62 for (int i = 0; i < s.size(); ++i)

63 { u32enter (v, c, n, s [i]);

64 }

65 return std:: u32string (v.begin(), v.end());

66 }

67

68 // the 10 bits of c-0 x10000 are stored in two surrogate code units

69 // 110110 xxxxxxxxxx 110111 xxxxxxxxxx

70 // 0xd800 -0xdbff 0xdc00 -9 xdfff

71 std:: u16string toUtf16 (std:: u32string s) // u32 -> u16

72 { std::vector <char16_t > v;

73 for (int i = 0; i < s.size(); ++i)

74 { if (s [i] < 0xd800 || (0xe000 <= s [i] && s [i] < 0x10000))

75 { v.push_back (static_cast <char16_t >(s [i]));

76 }

77 else

78 { char32_t c = s [i];

79 c -= 0x10000;

80 v.push_back (static_cast <char16_t >((c >> 10) | 0xd800));

81 v.push_back (static_cast <char16_t >((c & 0x3ff) | 0xdc00));

82 } }

83 return std:: u16string (v.begin(), v.end());

84 }

85

86 std:: u32string toUtf32 (std:: u16string s) // u16 -> u32

87 { std::vector <char32_t > v;

Page 20: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

20 2. PROZEDURALE PROGRAMMIERUNG

88 for (int i = 0; i < s.size(); ++i)

89 { if ((s [i] & ~0x3ff) == 0xd800 && i < s.size() - 1 && (s [i+1] & ~0x3ff) == 0xdc00)

90 { v.push_back (0 x10000 + (((s [i] & 0x3ff) << 10) | (s [i+1] & 0x3ff)));

91 ++i;

92 }

93 else

94 { v.push_back (s [i]);

95 }

96 }

97 return std:: u32string (v.begin(), v.end());

98 }

99

100 #ifdef testmain

101 int main()

102 { std:: string s8 (u8"z\u00df\u6c34\U0001f34c\u20ac"); // z &szlig; shui emoticon euro

103 std:: u16string s16 (u"\u4f60\u597d"); // ni hao

104 std:: u16string s16a (u"z\u00df\u6c34\U0001f34c\u20ac"); // z &szlig; shui emoticon euro

105 std:: u32string s32 (U"z\u00df\u6c34\U0001f34c\u20ac"); // z &szlig; shui emoticon euro

106

107 std::vector <char > v;

108 u8enter (v, 65, 0x80); std::cout << std:: string (v.begin(), v.end()) << std::endl;

109 u8enter (v, 0xdf , 0x80); std::cout << std:: string (v.begin(), v.end()) << std::endl;

110 u8enter (v, 0x6c34 , 0x80); std::cout << std:: string (v.begin(), v.end()) << std::endl;

111 u8enter (v, 0x1f34c , 0x80); std::cout << std:: string (v.begin(), v.end()) << std::endl;

112

113 std:: string x;

114 std:: u32string y;

115 std:: u16string z;

116

117 x = toUtf8 (s32); std::cout << x << std::endl;

118 y = toUtf32 (x);

119 if (s32 != y) std::cout << "wtf" << std::endl;

120

121 x = toUtf8 (s16a); std::cout << x << std::endl;

122 z = toUtf16 (x);

123 if (s16a != z) std::cout << "wtf" << std::endl;

124

125 x = toUtf8 (s16); std::cout << x << std::endl;

126 z = toUtf16 (x);

127 if (s16 != z) std::cout << "wtf" << std::endl;

128

129 y = toUtf32 (s16a); std::cout << toUtf8(y) << std::endl;

130 z = toUtf16 (y);

131 if (s16a != z) std::cout << "wtf" << std::endl;

132

133 y = toUtf32 (s16); std::cout << toUtf8(y) << std::endl;

134 z = toUtf16 (y);

135 if (s16 != z) std::cout << "wtf" << std::endl;

136

137 z = toUtf16 (s32); std::cout << toUtf8(z) << std::endl;

138 y = toUtf32 (z);

139 if (s32 != y) std::cout << "wtf" << std::endl;

140 }

141 #endif

Im nächsten Beispiel wird uconvert.cpp verwendet, um verschiedene Dateiformate mit Unicode-Text zu lesen undnach UTF-8 zu konvertieren. Alle Dateien müssen mit einer Byte-Order-Mark (BOM) markiert sein, andernfallswird UTF-8 angenommen. Streng genommen ist die BOM in UTF-8 verboten.

Alle Casts müssen stehen, sonst werden Vorzeichen beim Schieben falsch dupliziert. Alle Klammern müssen stehen,sonst klammert der Compiler falsch.

Page 21: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

10. SCHRIFTZEICHEN, STRINGS UND UNICODE 21

UTF-8 wird als Ein- und Ausgabeformat für die Konsole verwendet. UTF-32 ist als Zwischenformat für die schnelleStringverarbeitung geeignet.

Die C++-Fähigkeiten werden ausgenutzt (vector, iterator), aber nur gemäÿigt, um den Code nicht zu unlesbar zumachen. In auskommentierten Debug-Anweisungen kommen λ-Funktionen und for_each vor.

1 // unicode.cpp

2 // clang ++ -std=c++11 -c uconvert.cpp

3 // clang ++ -std=c++11 -Wall -pedantic unicode.cpp uconvert.o

4

5 #include <iostream >

6 #include <fstream >

7 #include <string >

8 #include <algorithm >

9 #include <iterator >

10 #include <vector >

11 //#include <codecvt > introduced 2011, deprecated 2017

12

13 #include "uconvert.h"

14

15 std:: string doutf32be (char const * fn)

16 { std::cout << "utf32be" << std::endl;

17 std:: ifstream f (fn , std::ios::in | std::ios:: binary);

18 std::vector <char > b (std:: istreambuf_iterator <char >(f), (std:: istreambuf_iterator <char >()));

19 f.close ();

20 std::vector <char32_t > v;

21 for (int i = 0; i < b.size() -3; i += 4)

22 { v.push_back (static_cast <char32_t >(((((((( unsigned char)b[i+0]<<8) | (unsigned char)b [i+1])

<<8)

23 | (unsigned char)b [i+2]) << 8) | (unsigned char)b [i

+3])));

24 }

25 // std:: for_each (v.begin(), v.end(), []( char32_t & c){ std::cout << " " << std::hex << (

unsigned int) c << std::dec; });

26 // std::cout << std::dec << std::endl;

27 return toUtf8 (std:: u32string (v.begin(), v.end()));

28 }

29

30 std:: string doutf32le (char const * fn)

31 { std::cout << "utf32le" << std::endl;

32 std:: ifstream f (fn , std::ios::in|std::ios:: binary);

33 std::vector <char > b (std:: istreambuf_iterator <char >(f), (std:: istreambuf_iterator <char >()));

34 f.close ();

35 std::vector <char32_t > v;

36 for (int i = 0; i < b.size() -3; i += 4)

37 { v.push_back (static_cast <char32_t >(((((((( unsigned char)b[i+3]<<8) | (unsigned char)b [i+2])

<<8)

38 | (unsigned char)b [i+1]) << 8) | (unsigned char)b [i

+0])));

39 }

40 std::cout << b.size() << " " << v.size() << std::endl;

41 return toUtf8 (std:: u32string (v.begin(), v.end()));

42 }

43

44 std:: string doutf16be (char const * fn)

45 { std::cout << "utf16be" << std::endl;

46 std:: ifstream f (fn , std::ios::in|std::ios:: binary);

47 std::vector <char > b (std:: istreambuf_iterator <char >(f), (std:: istreambuf_iterator <char >()));

48 f.close ();

49 std::vector <char16_t > v;

50 for (int i = 0; i < b.size() -1; i += 2)

51 { v.push_back (static_cast <char16_t >((((( unsigned char)b [i+0]) << 8) | (unsigned char)b [i+1])

));

Page 22: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

22 2. PROZEDURALE PROGRAMMIERUNG

52 }

53 std::cout << b.size() << " " << v.size() << std::endl;

54 return toUtf8 (std:: u16string (v.begin(), v.end()));

55 }

56

57 std:: string doutf16le (char const * fn)

58 { std::cout << "utf16le" << std::endl;

59 std:: ifstream f (fn , std::ios::in|std::ios:: binary);

60 std::vector <char > b (std:: istreambuf_iterator <char >(f), (std:: istreambuf_iterator <char >()));

61 f.close ();

62 std::vector <char16_t > v;

63 for (int i = 0; i < b.size() -1; i += 2)

64 { v.push_back (static_cast <char16_t >((((( unsigned char)b [i+1]) << 8) | (unsigned char)b [i+0])

));

65 }

66 std::cout << b.size() << " " << v.size() << std::endl;

67 return toUtf8 (std:: u16string (v.begin(), v.end()));

68 }

69

70 std:: string doutf8 (char const * fn)

71 { std::cout << "utf8" << std::endl;

72 std:: ifstream f (fn , std::ios::in|std::ios:: binary);

73 std::vector <char > b (std:: istreambuf_iterator <char >(f), (std:: istreambuf_iterator <char >()));

74 f.close ();

75 std::cout << b.size() << std::endl;

76 return std:: string(b.begin(), b.end());

77 }

78

79 std:: string filetype (char const * fn)

80 { std:: ifstream f (fn , std::ios::in|std::ios:: binary);

81 if (!f) return std:: string ();

82 if (!f.is_open ()) return std:: string ();

83 char c [4];

84 f.read (c, 4);

85 for (int i = f.gcount (); i < 4; ++i) c [i] = '\0';

86 for (int i = 0; i < 4; ++i) std::cout << std::hex << (unsigned int )(unsigned char) c [i] << "

| ";

87 std::cout << std::dec << std::endl;

88 f.close ();

89 if (c[0] == (char)0 && c[1] == (char)0 && c[2] == (char)0xfe && c[3] == (char)0xff)

90 { return doutf32be (fn);

91 }

92 if (c[0] == (char)0xff && c[1] == (char)0xfe && c[2] == (char)0 && c[3] == (char)0)

93 { return doutf32le (fn);

94 }

95 if (c[0] == (char)0xfe && c[1] == (char)0xff)

96 { return doutf16be (fn);

97 }

98 if (c[0] == (char)0xff && c[1] == (char)0xfe)

99 { return doutf16le (fn);

100 }

101 if (c[0] == (char)0xef && c[1] == (char)0xbb && c[2] == (char)0xbf)

102 { return doutf8 (fn);

103 }

104 return doutf8 (fn);

105 }

106

107 int utf8Chars (std:: string const & s)

108 { int n = 0;

109 for (int i = 0; i < s.size(); ++i)

110 { if ((s [i] & 0xc0) != 0x80) ++n;

111 }

Page 23: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

11. EIN- UND AUSGABE 23

112 return n;

113 }

114

115 std:: string toline (std:: string const & s)

116 { std:: string t (s);

117 for (int i = 0; i < t.size(); ++i)

118 { if (static_cast <unsigned char >(t [i]) < ' ') t [i] = '/';

119 }

120 return t;

121 }

122

123 int main (int argc , char** argv)

124 { std:: string s;

125 for (int i = 1; i < argc; ++i)

126 { s = filetype (argv [i]); // filetype analyses BOM and processes file content to UTF -8 string

127 std::cout << argv [i] << ": s=" << s.size () << "/c=" << utf8Chars (s) << std::endl;

128 std::cout << toline(s) << std::endl;

129 std::cout << std::endl;

130 } }

Beide Beispiele machen Gebrauch von ganzzahligen Bitoperationen. In aller Kürze (mit . werden unwichtige 0-Bitsmarkiert):

1 &: Bitweises Und , 01110100 & 11001100 -> 01000100 0x74 & 0xcc -> 0x44

2 |: Bitweises Oder , 01110100 | 11001100 -> 11111100 0x74 | 0xcc -> 0xfc

3 ~: Bitweises Nicht , ~01110100 -> 10001011 ~0x74 -> 0x8b

4 <<: Links schieben , 01110100 << 2 -> 11010000 0x74 << 2 -> 0xd0

5 >>: Rechts schieben , 01110100 >> 2 -> 00011101 0x74 >> 2 -> 0x1d

6 >>: unsigned , 11001100 >> 3 -> 00011001 0xcc >> 3 -> 0x19

7 >>: signed , 11001100 >> 3 -> 11111001 0xcc >> 3 -> 0xf9

8 & als Bitmaske: 10101010 & 00011100 -> ...010.. 0xaa & 0x1c -> 0x08

9 | als Bitkombination: 10101... | .....101 -> 10101101 0xa8 | 0x05 -> 0xad

11. Ein- und Ausgabe

Ein- und Ausgaben werden statt von der Bibliothek <cstdio> (früher <stdio>) von der objektorientiert program-mierten Bibliothek <iostream> abgewickelt. Die Leistungen beider Bibliotheken sind sehr ähnlich; die Schreib-weisen der Anweisungen sind jedoch gravierend verschieden. Die Ein- und Ausgabefunktionen der Bibliotheken<cstdio> und <iostream> sollten nicht im selbem Programm zusammen verwendet werden; grundsätzlich soll-ten in C++-Programmen die Ein- und Ausgabenfunktionen der C-Bibliothek <cstdio> überhaupt nicht mehrverwendet werden.

1 // io.cpp

2 #include <iostream >

3

4 using namespace std;

5

6 int main ()

7 { int a, b, summe;

8

9 cout << "\nBerechnung von a + b; Gib a und b: ";

10 cin >> a >> b;

11 summe = a + b;

12 cout << "\n" << a << "+" << b << "=" << summe;

13

14 cerr << "\nNormales Programmende";

15 return 0;

16 }

Die Ein- und Ausgabe in C und C++ ist stromorientiert gestaltet. Jedes Gerät oder jede Datei wird als Zeichen- oderByte-Strom interpretiert, der im Fall der Ausgabe erzeugt wird und im Fall der Eingabe gelesen und abschnittsweiseinterpretiert wird.

Page 24: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

24 2. PROZEDURALE PROGRAMMIERUNG

Die Funktionen der C-Bibliothek <cstdio> sind auch in C++ verfügbar und können dort aufgerufen werden, ob-wohl das kein guter C++-Stil ist. Für C++ existieren neu gestaltete C++-Bibliotheken <iostream>, <iomanip>,<fstream> und <sstream> (früher <strstream>), welche die C-Bibliothek ersetzen. Sie stellen ausgezeichneteBeispiele für Klassen dar und sollten statt der C-Bibliothek verwendet werden. Weiter gibt es Krücken und Spezi-albibliotheken wie <iosfwd>, <streambuf>, <istream> und <ostream>.

Wer auf hohe Compilergeschwindigkeit Wert legt, sollte in jeder Übersetzungseinheit sehr genau nur die benötigtenBibliotheken angeben, da sonst in C++ jedesmal sehr viel unnötiger Ballast mitübersetzt wird, der nicht unbe-trächtliche Übersetzungszeit beansprucht. Oft genügt einfach <iosfwd>, aber das können Sie selbst leider erst danngenauer beurteilen, wenn Sie den objektorientierten Teil des Kurses beherrschen.

11.1. Standardströme und Ein-/Ausgabeoperatoren. Tastatureingabe und Bildschirmausgabe werdenüber vier in <iostream> vorde�nierte Ströme cin, cout, cerr und clog abgewickelt. Weitere vier Ströme stehenfür Zeichenströme mit mehr als acht Bit pro Zeichen bereit. In der C-Bibliothek gibt es nur drei verschiedeneÄquivalente.

i/ostream wi/ostr. cstdio Bedeutung Gerätcin wcin stdin Standardeingabe normalerweise Tastatureingabecout wcout stdout Standardausgabe normalerweise Bildschirmausgabecerr wcerr stderr Fehlermeldungen normalerweise Bildschirmausgabeclog wclog stderr Protokollmeldungen normalerweise Bildschirmausgabe

Für char16_t und char32_t fehlen entsprechende Ströme.

In einigen Betriebssystemen (Unix) oder speziellen Situationen (MS-DOS bei der Ausgabeumlenkung) könnenNormalausgaben und Fehlermeldungen getrennt behandelt werden. Innerhalb des Programms werden sie danndurch cout und cerr unterschieden.

Ausgaben werden mit dem Ausdruck strom << e1 << e2 << ... << en erstellt, Eingaben mit dem Ausdruckstrom >> v1 >> v2 >> ... >> vn gelesen. Es gelten die C-Vorrangregeln, so daÿ manchmal � bei Vergleichen �geklammert werden muÿ. Die Gröÿen ei sind Rechenausdrücke, die Gröÿen vi die zu füllenden Variablen.

Bei der Eingabe können Adressen (l-Werte!) von Variablen mit den elementaren Typen

char * non-white-space Zeichenfolge (mit '\0')(signed/unsigned) char Einzelzeichen(short/long) (signed/unsigned) int Ganze Zahl�oat/(long) double Reelle Zahlbool logische Gröÿenchar/wchar_t Schriftzeichenvoid * Zeiger

gelesen werden.

Vorsicht: Das folgende Beispiel mit einer wohlbekannten C-Falle zeigt, daÿ der Programmierer sicherstellen muÿ,daÿ bereitgestellter Speicherplatz nicht überbeansprucht wird. Das Programm wird korrekt übersetzt und vonmanchen Compilern sogar noch ausgeführt. GNU-C erzeugt einen Laufzeitfehler; Borland-C überschreibt jedenSpeicherbereich, auf den p zufällig zeigt, sogar den setup-Bereich.

1 // io3.cpp

2 #include <iostream >

3 #include <iomanip >

4

5 using namespace std;

6

7 int main ()

8 { char * p; // string ist immer besser!

9 // cin >> p; // geht , ist aber ein Fehler , da ins Nirwana geschrieben wird

10 // fuehrt zu segmentation fault

11 // keine exception

12 p = static_cast <char*>("12345678");

13 cout << p << endl;

14 cin >> p; // geht , wenn maximal 8 Zeichen getippt werden und *p nicht readonly

15 cout << p << endl;

Page 25: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

11. EIN- UND AUSGABE 25

16 char w [1000];

17 cin >> w >> ws; // geht wahrscheinlich , wenn space vor dem 1000. Zeichen kommt

18 cout << w << endl;

19 cin.getline (w, 1000); // ist 100%ig korrekt

20 cout << w << endl;

21 cin >> std::setw (1000) >> w; // ist ebenfalls 100%ig korrekt

22 cout << w << endl;

23 return 0;

24 }

In die Ausgabe können Ausdrücke mit den Typen

char * Zeichenfolge mit \0(signed/unsigned) char Einzelzeichen(short/long) (signed/unsigned) int Ganze Zahl�oat/(long) double Reelle Zahlbool logische Gröÿenchar/wchar_t Schriftzeichenvoid * Zeiger (Maschinenadresse)

geschrieben werden.

Beispiele:

1 // io1.cpp

2 #include <iostream >

3

4 using namespace std;

5

6 int main ()

7 { char ch;

8

9 do

10 { cin >> ch;

11 cout << "Gelesen wurde Zeichen " << ch

12 << " in die Variable auf Adr. " << (const void*) &ch << endl;

13 } while (ch != '.'); /* Abfrage auf '\n' geht nur ohne "eatws" */

14 return 0;

15 }

Die obige Beschreibung bezieht sich auf <iostream>. Weitere Inserter sind in anderen Libraries de�niert. AlsBeispiel können natürlich auch Strings übertragen werden:

1 // io5.cpp

2 #include <iostream >

3 #include <string >

4

5 using namespace std;

6

7 int main ()

8 { string s;

9 cout << "Gib Namen:" << endl;

10 cin >> s;

11 cout << "Hallo " << s << "!" << endl;

12 return 0;

13 }

11.2. Fehlerbehandlung. Jedem Strom ist ein Stromzustand zugeordnet, der die Stromfehler der vorherge-henden Stromzugri�e speichert und für den Erfolg der nächsten Stromzugri�e abgefragt werden sollte. Prinzipiellexistieren drei Möglichkeiten, diesen Stromzustand abzufragen:

Stromname oder -operation Erfolg oder MiÿerfolgZustandsabfragen Genaue InformationBitzugri� auf die Stromzustandsvariable Direkte Manipulation

Page 26: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

26 2. PROZEDURALE PROGRAMMIERUNG

Die einfachste Möglichkeit ist die Verwendung des Stromnamens oder einer Stromoperation als Bedingung. DerWert Null (false, == 0) signalisiert einen Übertragungsfehler, ein Wert verschieden von Null (true, != 0) der Erfolgder letzten Operation.

Damit sind Eingabeschleifen der Formwhile (cin � x) cout � endl � x;

und Prüfungen der Formif (!(cout � x)) /* Ausgabe klappt nicht */

möglich.

Alternativ kann man Funktionen zur direkten Abfrage des Stromzustandes aufrufen. Der Aufruf erfolgt immer inder Form strom.frage (). Die Funktionen liefern Informationen über den Ablauf der vergangenen Operationenund (wichtiger) einen Hinweis auf die Durchführbarkeit weiterer Operationen. Für bestimmte Fragen müssen dieElementaroperationen kombiniert werden.

cin.eof () end of �le seen Dateiende erreichtcin.fail () next will fail keine Operationen mehr möglichcin.bad () stream corrupted Schwerer Fehlercin.good () next might succeed "cin.good () || cin.eof () last succeed letzte Operation ok

Falls ein nicht allzu schwerer Fehler in einer der vergangenen Operationen aufgetreten ist, kann der Strom miteinem Aufruf von cin.clear () wieder in einen arbeitsfähigen Zustand gebracht werden. Ob das sinnvoll ist, muÿder Programmierer entscheiden. Nach einem Dateiende von der Tastatur (Windows: ^Z, Linux: ^D) ermöglichtcin.clear () manchmal weitere Eingaben.

Die letzte Möglichkeit der Zustandsabfragen sind direkte Manipulationen der Zustandsbits. Die Funktion cin.rdstate ()

liefert den momentanen Stromzustand als ganze Zahl vom Typ int. Da die Interpretation der einzelnen Zustands-bits nicht festgelegt ist, wohl aber die Zustände und Zustandsnamen, sollen Zustandsabfragen nur mit Hilfe derZustandsnamen erfolgen. Es gibt die folgenden Zustände

ios::goodbit

ios::eofbit

ios::failbit

ios::badbit

Beispiel:

1 // io2.cpp

2 #include <iostream >

3

4 using namespace std;

5

6 int main ()

7 { ios:: iostate s;

8 s = cin.rdstate ();

9 if (s & ios:: eofbit) { cout << "cin Dateiende bei Start" << endl; }

10 int i;

11 cin >> i;

12 cout << "i=" << i << endl;

13 s = cin.rdstate ();

14 if (s & ios:: eofbit) { cout << "cin Dateiende" << endl; }

15 }

Die Methode wird nicht empfohlen und entspricht eigentlich einem schon um 1960 veralteten Programmierstil!

Parallel existiert natürlich cin.setstate (s).

Seit 1998 sind Exceptions in iostream eingearbeitet; sie fehlen jedoch noch hier.

11.3. Formatierung. Ohne weitere Angaben wird bei der Eingabe an Stellen, an denen dies sinnvoll ist,Leerraum (whitespace) übergangen. Die Ausgabe wird ähnlich vorformatiert, wie dies schon in C die Funktionprintf durchführt.

Page 27: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

11. EIN- UND AUSGABE 27

Diese Vorformatierung kann vom Programmierer jederzeit geändert werden. Auch dazu stehen drei Möglichkeitenzur Auswahl: Der Aufruf von Stromfunktionen zur Änderung des Formatierungszustandes, die Verwendung vonsog. Manipulatoren in einem Ausdruck mit einer Stromoperation und die direkte bitweise Änderung des Format-zustandes. Prinzipiell kann für jeden Strom jedwede Einstellung erzeugt werden. Jedoch sind viele Kombinationensinnlos und haben daher keine Wirkung. So wird ein Ausgabestrom nicht gelesen und kann daher auch keinenLeerraum überlesen. Durch cin und cout wird angezeigt, bei welchem Strom ein Aufruf sinnvoll ist.

Am einfachsten ist der Aufruf von Formatierungsfunktionen.

Die Funktionssignaturen (Argumente und Ergebnisse) folgen einer Systematik: Alle Funktionen liefern als Ergebnisden alten Zustand zurück, der ignoriert oder gespeichert werden kann (char ch = cout.fill(); ch = cout.fill ('*'); cout.fill ('x');).

Ohne Argumente bewirkt die Funktion eine Abfrage des eingestellten Zustandes.

Mit Argumenten wird zusätzlich der neue Zustand gemäÿ den Argumenten eingestellt.

cout.flags (l) stellt genau den übergebenen Zustand l ein. cout.setf(l) setzt lediglich die übergebenen Teil-zustände l. cout.setf(l, f) setzt nur die übergebenen Teilzustände l in der Zustandsgruppe f.

cout.unsetf(l) und cout.unsetf(l, f) löscht die übergebenen Teilzustände l (ev. in der Zustandsgruppe f).

Bei Funktionen mit identischer Wirkung existiert lediglich eine. (cout.flags() statt cout.setf()).

Bei der Beschreibung der Wirkung bedeuted fmtfl den internen Zustand.

Funktion Wirkungcin.getline (char *, int) Lesen einer Zeile mit beschränktem Platzcout.tie (cin) Koppeln von Eingabestrom an Ausgabestromcout.width (n) Einstellung der Feldbreite der nächsten Ausgabecout.width () Abfrage der Feldbreitecout.fill ('#') Einstellung des Füllzeichenscout.fill () Abfrage des Füllzeichenscout.precision (n) Einstellung der Nachkommastellencout.precision () Abfrage der Nachkommstellencout.flags (l) Einstellung des kompletten Formatzustandes auf den Wert l

Wirkung: fmtfl = l

l = cout.flags () Abfrage des Formatzustandescout.setf (l) Setzen einzelner Formatzustände

Wirkung: fmtfl |= l

cout.setf (l, f) Löschen von Zustandsgruppen undSetzen einzelner FormatzuständeWirkung: fmtfl = (l & f) | (fmtfl & ~f)

Bsp. cout.setf (ios::left, ios::basefield)

cout.unsetf (l) Löschen einzelner FormatzuständeWirkung: fmtfl &= ~l

Innerhalb eines Ein- oder Ausgabeausdruckes können wichtige dieser Einstellungen auch durch Manipulatorenerfolgen. In der Tabelle ist die mittlere Spalte verkürzt wiedergegeben. In Programmen muss selbstverständlichstatt setf (boolalpha) ausführlich cout.setf (ios_base::boolalpha) stehen.

Page 28: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

28 2. PROZEDURALE PROGRAMMIERUNG

Manipulator Ersatzfunktion Bedeutungboolalpha setf (boolalpha) bool alphabetisch (false, true)noboolalpha unsetf (boolalpha) bool numerisch (0, 1)showbase setf (showbase) Basis-Prä�x 0 oder 0xnoshowbase unsetf (showbase) kein Basis-Prä�xshowpoint setf (showpoint) Dezimalpunkt auch ohne Nachkommastellennoshowpoint unsetf (showpoint)

showpos setf (showpos) Vorzeichen auch bei positiven Zahlennoshowpos unsetf (showpos)

skipws setf (skipws) Whitespace vor Eingabe erlaubtnoskipws unsetf (skipws)

uppercase setf (uppercase) 0X1A und 1.0E8nouppercase unsetf (uppercase)

unitbuf setf (unitbuf) �ush nach jeder Operationnounitbuf unsetf (unitbuf)

internal setf (internal, adjustfield) links- und rechtsbündigleft setf (left, adjustfield) linksbündigright setf (right, adjustfield) rechtsbündigdec setf (dec, basefield) int dezimalhex setf (hex, basefield) int sedezimaloct setf (oct, basefield) int oktalfixed setf (fixed, floatfield) �oat ohne Exponentscientific setf (scientific, floatfield) �oat mit Exponentendl Neue Zeile und �ushends '\0'

flush Pu�er leeren und alles Vorhandene transportierensetw (n) Einstellung der Feldbreite der nächsten Ausgabesetprecision (n) Einstellung der Nachkommastellensetfill ('#') Einstellung des Füllzeichenssetbase (b) Einstellung des Zahlenbasis (8, 10, 16)setiosflags (l) Setzen einzelner Formatzuständeresetiosflags (l) Löschen einzelner Formatzustände

Die bitweisen Änderungen stellen eigentlich veralteten Programmierstil dar und sollten vermieden werden. Dieeinzelnen Bits werden durch einen Namen bezeichnet, falls sie doch verwendet werden müssen. Aber für alle, diees nicht lassen können, folgen in der letzten Tabelle die Bitnamen:

ios::skipws Überspringen von Leerraumios::left Linksbündige Ausgabe (Leerraum rechts)ios::right Rechtsbündige Ausgabe (Leerraum links)ios::internal Links- und rechtsbündige Ausgabe (Leerraum in die Mitte)ios::adjustfield Alle Bits zur Bündigkeit

ios::dec Dezimale Ein- und Ausgabeios::oct Oktale Ein- und Ausgabeios::hex Sedezimale Ein- und Ausgabeios::basefield Alle Bits zur Zahlenbasis

ios::showbase Anzeige der Zahlenbasisios::showpoint Dezimalpunkt bei reellen Zahlenios::uppercase Buchstaben in Zahlen groÿ schreibenios::showpos Pluszeichen immer schreibenios::scientific Gleitpunktschreibweise bei reellen Zahlenios::fixed Festpunktschreibweise bei reellen Zahlenios::floatfield Bits zur Schreibweise von reellen Zahlen

ios::unitbuf sofortige Ausgabeios::boolalpha bool alphabetisch (false, true)

Page 29: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

14. ATTRIBUTE 29

12. Ergänzungen

13. struct union enum

14. Attribute

Zeichensatz, digraphs, trigraphs, alternative tokensu

keywords

Page 30: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem
Page 31: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

KAPITEL 3

Objektorientierte Programmierung

Für vollständige Objektorientierung benötigt eine Programmiersprache drei Elemente:

• Datenkapselung: Klassen (class/struct/union/enum) in C++• Rechte: public/private/protected in C++• Polymorphie: Vererbung in C++

Der dritte Punkt wird in einem eigenen Kapitel behandelt.

1. Kapselung

Klassen in C++ werden verwendet, um neue Datentypen mit eigenen Wertebereichen zu erstellen. Mit diesen neuenDatentypen werden Variable zur Speicherung dieser Werte erzeugt. Diese Variablen heiÿen in der objektorientiertenProgrammierung Objekte1, manchmal selten auch Instanzen. Dieser Begri� darf mit der zweiten Bedeutung vonInstanz bei Templates nicht verwechselt werden.

Anders als Strukturen in C werden die Funktionen zur Verarbeitung dieser neuen Typen in der Klasse de�niertoder deklariert2.

Die Werte einer neuen Klasse können aus elementaren Datentypen oder anderen Klassen zusammengesetzt sein.

Alle Elemente einer Klasse werden als Member bezeichnet. Membervariable werden in der objektorientierten Pro-grammierung auch Felder (�elds) genannt; Memberfunktionen heiÿen auch Methoden (methods). Neben den Mem-bervariablen und Methoden kommen in Klassen weitere Membergruppen vor: Konstruktoren zum Erzeugen neuerObjekte, Destruktoren zur Speicherfreigabe nicht mehr benötigter Objekte, Memberoperatoren zur bequemen Pro-grammierung der Operationen mit den neuen Typen, statische Member für klassenbezogene Informationen undfriends für alles was sonst gar nicht geht.

Neben dieser eher technisch orientierten Einteilung der Member verwendet man in der Programmierung oft eineeher zielorientierte Gliederung: Hier bleiben Membervariable, Konstruktoren und Destruktoren erhalten; die üb-rigen Member werden unterteilt in die Zugri�soperationen (accessors) zum Abfragen der Variablenwerte, in dieManipulatoren (manipulators) zum gezielten Ändern der Variablenwerte, Konversionen zur Erzeugung andererDatentypen mit gleichen oder ähnlichen Werten, arithmetischen oder arithmetikähnlichen Operationen zur Ver-knüpfung mehrerer Werten des neuen Typs/mehrerer neuer Typen, Ein- und Ausgabeoperationen, Iteratoren fürKlassen mit Datenmengen.

Eine Sonderstellung nehmen wegen ihrer technischen Probleme und ihrer hohen Bedeutung die sog. Big Threeein: der Copy-Konstruktor, der Destruktor und der Wertzuweisungsoperator. Der Begri� Big Three meint in C++immer genau diese drei speziellen Operationen.

2. Operatorfunktionen

Die in C und C++ vorhandenen Operatoren können gröÿtenteils in C++ mit neuen Bedeutungen versehen werden.Dieser Vorgang heiÿt Überladen von Operatoren (operator overloading). Damit ergibt sich die bequeme Möglichkeit,auch bei neuen Datentypen die üblichen Rechenoperationen verwenden zu können, ohne wie in C auf Funktions-aufrufe zurückgreifen zu müssen.

Beispiel:

1In C sind Objekte speicherverbrauchende Sprachelemente, also Variable oder Funktionen. In C++ sind Objekte Variable von als

Klassen de�nierter Datentypen2Wie in C ist eine Deklaration die Bekanntgabe der Existenz einer Gröÿe. Eine De�nition ist die vollständige Beschreibung einer

Gröÿe mit allen ihren Eigenschaften.

31

Page 32: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

32 3. OBJEKTORIENTIERTE PROGRAMMIERUNG

Abbildung 1. Klassen in C++

1 matrix A, B, C, D, h;

2 ...

3 D = A / 2 + B * C; // statt: h = MatMulDbl (A, 0.5); D = MatMul (B, C); D =

4 MatAdd (h, D);

2.1. Operatorfunktionen. In C++ gilt jede Operatorverwendung (x⊙y, ⊙x, x⊙) als Funktionsaufruf einerdem Operator ⊙ zugeordneten Operatorfunktion. Diese Operatorfunktion wird mit operator⊙ bezeichnet. DasWort operator ist ein Schlüsselwort. Statt x⊙ y kann man also auch operator⊙ (x, y) schreiben.

Beispiel:

1 int i, j, k;

2 ...

3 operator = (k, operator $+$ (i, j)); // wirkt wie k = i $+$ j;

Page 33: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

2. OPERATORFUNKTIONEN 33

Diese Operator-Funktionen können wie normale Funktionen überladen werden. Damit der Compiler die aufzuru-fende Funktion eines überladenen Operators eindeutig feststellen kann, muÿ lediglich mindestens ein Argument desOperators ein neuer Datentyp, also eine C++-Klasse oder eine Aufzählung sein. Alternativ kann der Operator einemember-Funktion sein.

Die überladbaren Operatoren sindnew delete new [] delete []

+ - * / % ^ & | ~

! = < > += -= *= /= %=

^= &= |= << >> >>= <<= == !=

<= >= && || ++ -- , ->* ->

() [] ""id (typ)

Nicht überladen werden dürfen die Operatoren . .* :: ?: und die Präprozessorsymbole # ##.

Beim Überladen wird lediglich die Bedeutung der Operatoren auf neue Datentypen erweitert; die schon in Cvorgegebene Stellung, die Priorität, die Assoziativität und die Argumentanzahl bleibt unverändert. Es ist also nichtmöglich, einen binären Operator ! (zwei Argumente statt eines), einen Post�x-Operator ! als Fakultätsoperator (notist Prä�x), einen Prä- oder Post�x-Operator % (% ist binär) zu erzeugen; genausowenig kann � von rechts nach linksklammern oder eine Operatorfunktion operator + (int, complex, int) mit drei Argumenten erzeugt werden.

Die beiden in C miteinander verwandten Operatoren x ⊙ y und x ⊙= y gelten nach einer Überladung als ver-schiedene Operatoren. Wenn die C-Relation weiter gelten soll, muÿ sie getrennt und korrekt für x ⊙ y und für x⊙= y programmiert werden.

Ein überladener Operator wird wie eine normale Funktion deklariert und de�niert, nämlich mitreturntyp operator ⊙ (Argumentliste) { Funktionscode }

Beispiel:

class X {

int i; public:

int operator + (int);

X operator + (X);

};

int X::operator + (int j)

{ return i + j;

}

X X::operator + (X x)

{ X h; h.i = i + x.i; return h;

}

X operator + (X x)

{ return x;

}

2.2. Übersicht über die Operator-Funktionen. Die folgende Übersicht zeigt alle Möglichkeiten, Opera-toren zu überladen. Post�xoperatoren müssen mit einem zusätzlichen künstlichen int-Parameter versehen werden,um sie von den Prä�xoperatoren zu unterscheiden. Dieser int-Parameter hat jedoch keine weitere Bedeutung.

Die speziell aufgeführten Memberoperatoren (typ, =, [], (), →) dürfen nicht global de�niert werden.

Die Tabelle zeigt, daÿ die Symmetrie der kommutativen Operatoren (+ *) gestört wird, wenn sie als member-Funktionen de�niert werden. x + y (x.operator + (y)) und y + x (y.operator + (x)) sind nicht notwendiggleich, wenn X x; und Y y; aus verschiedenen Klassen oder Typen kommen.

2.3. Empfehlungen. Alle überladenen Operatoren sollten mit möglichst wenig Überraschungse�ekten pro-grammiert werden. Wenn der Operatorcomplex complex::operator * (complex c)

{ complex h; h.r = r - c.r; h.i = i - c.i; return h; }

die komplexe Subtraktion durchführt, werden die darauf beruhenden Programme völlig unverständlich.

Page 34: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

34 3. OBJEKTORIENTIERTE PROGRAMMIERUNG

Tabelle 1. Globale und Member-Operatoren

Scope des Operators Deklaration Operator-Aufruf Funktionsaufrufglobaler Operator T1 operator ⊙ (T2, T3) t2 ⊙ t3 operator ⊙ (t2, t3)globaler Operator T1 operator ⊙ (T2) ⊙ t2 operator ⊙ (t2)globaler Operator T1 operator ⊙ (T2, int) t2 ⊙ operator ⊙ (t2, 0)member-Operator T1 X::operator ⊙ (T2) x ⊙ t2 x.operator ⊙ (t2)member-Operator T1 X::operator ⊙ () ⊙ x x.operator ⊙ ()member-Operator T1 X::operator ⊙ (int) x ⊙ x.operator ⊙ (0)member-Operator X::operator typ () (typ) x x.operator typ ()member-Operator T1 X::operator = (T2) x = t2 x.operator = (t2)member-Operator T1 X::operator [] (T2) x [t2] x.operator [] (t2)member-Operator T1 X::operator () (T2, T3, ...) x (t2, t3, ...) x.operator () (t2, t3, ...)member-Operator T1 X::operator → () x → m (x.operator → ()) → m

Selbst geübte C-Programmierer werden verwirrt sein, wenn der Operatorcomplex complex::operator + (complex c)

{ r += c.r; i += c.i; return *this; }

sein Argument zum Klassenobjekt addiert; diese Leistung sollte dem Operator += vorbehalten bleiben.

Ein Grenzfall ist sicherlich eine Klasse integer für ganze Zahlen (mit gröÿerer Länge als long), wenn member-Operatoren für den ggT und das kgV benötigt werden. Beide Funktionen haben zwei Operanden, so daÿ sich diebinären Operatoren anbieten. Die arithmetischen (+-*/%) und relationalen (< > <= >= == =!) Operatoren, die Zu-weisungsoperatoren (= += -= *= /= %= ^= &= |= <<= >>=) und die Ein- und Ausgabeoperatoren (<< >>) schei-den aus, da sie in ihrer Normalbedeutung gebraucht werden. So bleiben die Operatoren ^ & | && || , ->* -> () []

übrig, von denen sich aus verbandtheoretischen Gründen ({N, ggT (x, y) = x ⊓ y = x · y = x&y, kgV (x, y) =x ⊔ y = x + y = x∥y} ist ein distributiver Verband) die Operatoren & | && || anbieten. Wahrscheinlich hängtdie Entscheidung von der Zielgruppe ab, für die diese Klasse programmiert wird: Falls sie von Mathematikern be-nutzt wird, ist die Wahl dieser Operatoren bei guter Kommentierung unproblematisch; falls sie jedoch auch vonNicht-Mathematikern angewendet wird, ist die Verwendung von member-Funktionen integer ggT (integer,

integer) und integer kgV (integer, integer) vorzuziehen, da vor allem C-Programmierer bei (integer & |)an eine bitweise Operation und bei (integer && ||) an eine boolsche Operation denken.

2.4. Arithmetische Operatoren. Arithmetische Operatoren (+ - * / %) sollten nur für Klassen program-miert werden, in denen analoge mathematische Verknüpfungen bestehen. Beispiele sind numerische Klassen fürBrüche, komplexe Zahlen oder Quaternionen, und algebraische Klassen für Vektorräume oder Polynome. Dabeisollten sowohl die C-Eigenschaften dieser Operatoren als auch die Eigenschaften der programmierten Verknüpfun-gen berücksichtigt werden. In nichtmathematischen Klassen können natürlich einzelne Operatoren programmiertwerden, die für diese Klassen sinnvoll sind (+ als Konkatenation in Stringklassen, || als Parallelitätsabfrage ingeometrischen Klassen).

Meistens sollten dann die zugehörigen Zuweisungs-Operatoren (+= -= *= /= %=) ebenfalls programmiert werden.

Oft müssen Verknüpfungen verschiedener Datentypen (complex + int) oder Datenquali�kationen (complex +

const int) möglich sein , um ein bequemes Arbeiten mit der Klasse zu ermöglichen.

Die Addition und Multiplikation in C ist kommutativ; a + b darf vom Compiler sogar als b + a ausgeführt werden.Damit ist ein member-Operator eigentlich schon ausgeschlossen. Denn mit dem Objekt X x der Klasseclass X { public: X operator + (int); };

kann man x + 3 ≡ x.operator + (3), aber nicht 3 + x ≡ 3.operator + (x) schreiben. Dieser Operator müÿtemember-Operator von int sein � int ist jedoch keine Klasse und könnte auch keine nachträglich eingefügtenweiteren Operatoren haben.

Am elegantesten ist meistens die Lösung, die Zuweisungsoperatoren als member-Operatoren zu programmieren,die arithmetischen Operatoren als globale Operatoren auf die Zuweisungsoperatoren zurückzuführen und andereDatentypen mit Hilfe geeigneter Konstruktoren zu konvertieren.

class X {

public:

X (const long) { *this = (X) l; }

Page 35: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

2. OPERATORFUNKTIONEN 35

X operator += (X);

};

X X::operator += (X x)

{ *this += x; return *this; }

X operator + (const X a, const X b)

{ X h; h = a; h += b; return h; }

Denn jetzt sind im aufrufenden Programm alle folgenden Aufrufe erlaubt:

int i; long l; X x; const X y;

x + y;

x + i; i + x;

x + l; l + x;

x + 3; 3 + x;

Ein weiterer Vorteil ist, daÿ die Addition nur noch einmal, nämlich bei += programmiert werden muÿ.

Die Vorteile werden mit einem leichten Geschwindigkeitsverlust erkauft, weil bei jedem Operator-Aufruf von + zweiOperator-Funktionen aufgerufen werden.

2.5. Increment und Decrement.

2.6. Bitoperatoren. C und C++ verfügen über überladbare bitweise Verknüpfungen ^ | & ~ << >>. DieSchiebeoperatoren sind in C++ der Ein- und Ausgabe gewidmet. Sie können trotzdem in anderen Klassen eigeneAufgaben übernehmen. Schon in C sind sie nicht kommutativ (a<<b ̸= b<<a); diese Eigenschaft dürfen in eigenenImplementierungen übernommen werden.

Manchmal kommt die Idee auf, den operator^ als Potenzoperator zu überladen (a^b = ab). Leider verstossen dabeidie Klammerungsregeln gegen die mathematischen Gewohnheiten; deshalb bewährt sich diese Überladung nicht:

a^b + c = a^(b + c) ̸= ab + c

a^b^c = (a^b)^c ̸= abc

Die Bitoperatoren werden selten überladen.

2.7. Ein- und Ausgabeoperatoren. Seit Entwicklung der iostream-Library wurden die beiden Operatoren<< und >> für die Ein- und Ausgabe überladen. Ihre genauen Bezeichnungen sind stream-inserter und stream-extractor. In neuen Klassen X sollten sie keine Member-Operatoren sein, da sie dann mit X-Objekten x aufgerufenwerden müssten: x.operator<<(rs) oder x << rs; Die linke Seite sollte jedoch immer der Stream sein.

Die Operatoren sollten also global de�niert werden. Dann haben sie keinen Zugri� auf die privaten Member von Xmehr; statt dessen sind sie auf Accessor-Funktionen angewiesen. Die Möglichkeit, sie als friend in den Klassen-Scopeaufzunehmen, gilt als unschön und veraltet.

Die globale De�nition hat einen Nachteil bei der Vererbung: Globale Funktionen werden nicht vererbt und müssenauch in Subklassen neugeschrieben werden, in denen eigentlich keine Neuimplementierung notwendig wäre.

Die stream-Operatoren sind also global, haben als linkes Argument einen Stream und als rechtes Argument einObjekt einer Klasse X. Es ist üblich, sie kaskadieren zu können: cout << x << y << z;. Der Operator klammertvon links nach rechts (((cout << x) << y) << z;) � also muss jeder Aufruf das Streamobjekt zurückgeben. Dabeidürfen keinesfalls Duplikate der Streamobjekte entstehen: Zwei Filedeskriptoren auf dieselbe Datei können dieseDatei zerstören. Die Streamobjekte müssen also Referenzen sein und sie werden manipuliert, also verändert. DieReferenzen dürfen nicht konstant sein.

Die Stream-Operatoren verwenden prinzipiell Sentry-Objekte, um zu verhindern, dass notwendige Vor- und Nach-arbeiten vergessen werden.

Exkurs: Das Sentry-Muster

Mehrere Funktionen (f, g, . . . ) benötigen manchmal gemeinsame Vor- und Nacharbeiten (hier prework und postwork ge-nannt). Die Schreibweise

Page 36: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

36 3. OBJEKTORIENTIERTE PROGRAMMIERUNG

1 void f (void)

2 {

3 prework ();

4 // work of f

5 postwork ();

6 }

ist fehlerträchtig, weil die Gefahr besteht, bei vielen verschiedenen Implementierungen der Funktionen den Aufruf vonprework und postwork zu vergessen. Dieses Risiko kann halbiert werden, wenn man statt prework und postwork eine neueKlasse Sentry einführt, die im Konstruktor das prework und im Destruktor das postwork enthält. Erzeugt man am Beginnvon f ein Sentry-Objekt, wird das prework automatisch durch den Konstruktor aufgerufen. Das Postwork kann nicht mehrvergessen werden, da es mit Funktionsende automatisch im Destruktor des Sentry-Objekts aufgerufen wird.

1 class Sentry

2 {

3 public:

4 Sentry () { /* do prework here */ }

5 ~Sentry () { /* do postwork here */ }

6 };

7

8 void f (void)

9 {

10 Sentry s;

11 // work of f() is always prefixed with s-ctor

12 // and postfixed with s-dtor

13 } // implicit call of s-dtor

Sentries können Klassen ohne Daten sein; sind Daten zu speichern, werden sie im Konstruktor übergeben, im Objektgespeichert und im Destruktor verwendet.

Vorteil: Weniger Schreibarbeit (eine Zeile statt zwei an verschiedener Stelle); weniger Fehlermöglichkeiten (postwork kannnicht vergessen werden);

Stream-Klassen nutzen Sentries.

In den Stream-Klassen istream und ostream sind Sentry-Klassen vorgesehen, die bei jeder Inserter- und Extractor-Überladung benutzt werden müssen. Sie benutzen die Streams als Datenobjekt. Die Sentry-Objekte haben einencast-operator nach bool, der der Benutzbarkeit der Streamoperatoren anzeigt. Er sollte immer abgefragt werden.

Damit sind jetzt alle Inserter- und Extractor-Bedürfnisse vollständig. Ein- und Ausgabe-Überladungen haben immerdie folgende Form:

1 ostream & operator << (ostream & s, X const & rs) // insert a const rs to stream s

2 {

3 ostream :: sentry se (s); // ctor does prework of s, dtor does postwork

4 if (se) // cast to bool signals noncorrupt stream

5 {

6 // do output of rs with getter -menbers to s

7 }

8 return s; // enable cascading of output operators cout << x << y << z;

9 }

1 istream & operator << (istream & s, X & rs) // extract data from stream s to nonconst object rs

2 {

3 istream :: sentry se (s); // ctor does prework of s, dtor does postwork

4 if (se) // cast to bool signals noncorrupt stream

5 {

6 // do input to rs with setter -menbers from s

7 }

8 return s; // enable cascading of input operators cin >> x >> y;

9 }

Wenn der Inserter selbst die Manipulatoren benutzt, sollte er sie am Ende wieder in den Originalzustand versetzen,der dazu gespeichert werden muss.

Page 37: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

2. OPERATORFUNKTIONEN 37

1 std::ios:: fmtflags f = s.setf (ios::right , ios:: adjustfield);

2 // h << setw (7) << ...

3 s.flags (f);

Manchmal sind Textdarstellungen eines Objekts komplexer und müssen aus mehreren Teilen zusammengesetztwerden. Wenn das mit mehreren Inserter-Aufrufen geschieht, wirken Manipulatoreinstellungen nur auf den er-sten. Damit die gesamte Textdarstellung von den Manipulatoreinstellungen erfasst wird, darf also nur ein einzigerInserter-Aufruf statt�nden. Damit wird es zwingend, die Textdarstellung erst in einem String komplett zu erfassen,der am Ende einmal ausgegeben wird. C++ folgt aus historischen Gründen demselben eigentlich unlogischen Prin-zip wie C, die Stringerzeugung in Streams zu erledigen, statt wie Java einfach eigene Funktionen bereitzustellen.Der Mechanismus wurde einmal überarbeitet und verbessert, die ältere, obsolete und deprecated Variante heiÿtstrstream, die aktuelle stringstream.

1 #include <sstream >

2

3 std:: ostringstream h;

4 std:: string t;

5 // h stores the text in possibly several calls

6 h <<

7 h <<

8 h <<

9 // at the end h is once converted to a string

10 t = h.str ();

11 // and inserted to the stream s

12 s << t;

Zusammengefasst kann ein Inserter etwa wie folgt aussehen:1 ostream & operator << (ostream & s, X const & rs) // insert a const rs to stream s

2 {

3 ostream :: sentry se (s); // ctor does prework of s, dtor does postwork

4 if (se) // cast to bool signals noncorrupt stream

5 {

6 std::ios:: fmtflags f = s.setf (ios::right , ios:: adjustfield);

7 std:: ostringstream h;

8 std:: string t;

9 // h stores the text in possibly several calls

10

11 // insert text representation to stringstream h

12

13 // at the end h is once converted to a string

14 t = h.str ();

15 // and inserted to the stream s

16 s << t;

17 s.flags (f);

18 }

19 return s; // enable cascading of output operators cout << x << y << z;

20 }

2.8. Logische Operatoren. Für die logischen Operatoren ! && || gelten analoge Regeln wie für die arith-metischen Operatoren.

Ausnahme: Für überladene Logikoperationen wird kein logischer Shortcut durchgeführt; der gesamte geschriebeneAusdruck wird immer ausgewertet.

Wichtig: Vor C++17 gelten die Logikoperatoren als kommutativ; d.h. es ist nicht garantiert, dass die linke Seitevor der rechten ausgewertet wird.

2.9. The Big Five/Three. Three or Five: Vor 2011 waren es nur 3, da die Move-Operatoren gefehlt haben.

Generalregel: Wenn einer dieser 5 Operatoren in einer Klasse existiert, müssen alle 5 implementiert werden.

Wenn einer fehlt (X() = delete;) oder durch den default ersetzt wurde (X() = default;), darf kein weitererdieser 5 existieren.

Page 38: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

38 3. OBJEKTORIENTIERTE PROGRAMMIERUNG

Kurz: Alle oder Keiner! Alles oder Nichts!

Die Wertzuweisungsoperatoren müssen Memberoperatoren sein. Sie dürfen nicht global de�niert werden. Ihre Auf-gabe ist es, geschickt das this-Objekt vom alten Inhalt leerzuräumen (eigentlich dtor-Aufgabe) und anschlieÿendmit einem neuen Inhalt aus der rechten Seite der Wertzuweisung zu füllen (eigentlich ctor-Aufgabe). Dabei sindauch die C-Regeln zu beachten (Kaskadierbarkeit x = y = z = X(), x = x.

Die Konstruktoren werden implizit und explizit aufgerufen; die impliziten Aufrufe können mit der Spezi�kationexplicit vom Programmieren unterbunden werden.

Der dtor � es gibt genau einen � wird nur implizit aufgerufen.

2.9.1. Copy ctor.

2.9.2. Move ctor.

2.9.3. Copy assignment. Der Wertzuweisungsoperator einer Klasse X folgt fast immer dem Muster:

1 X const & operator= (X const & rs) // references accelerate , const must be correct

2 {

3 if (this != & rs) // to allow x = x

4 {

5 free existing content of *this // dtor behaviour

6 generator new content from rs // copy ctor behaviour

7 }

8 return *this; // to allow cascading assignments x = y = z = X();

9 }

2.9.4. Move assignment.

2.9.5. Destructor. Wertzuweisung

o1 = o2

o = o

2.10. Konversionen. In Klassen können zwei Memberarten als Konversionen interpretiert werden: Konstruk-toren konvertieren Typen von auÿerhalb der Klasse in ein neues Objekt. Castoperatoren konvertieren ein Klassen-objekt in einen anderen Datentyp. Die Konversionen sollten keine Informationen verlieren, in manchen Einzelfällenist ein Informationsverlust jedoch erlaubt. Beim cast double x = (double) 3 können alle 32 Bit der int 3 in den53 Bit der double gespeichert werden =⇒ kein Informationsverlust. Umgekehrt gehen bei int n = (int) 3.14

alle Nachkommstellen verloren =⇒ partieller gewünschter Informationsverlust. Dasselbe gilt für if (x): aus der64 Bit reichen Information einer double wird die schwache Information Null oder nicht Null.

Spezielle Konversionen sind:

1 class X

2 {

3 X (std:: string) { konvertiert string in X-Objekt }

4 operator std:: string () const { konvertier X-Objekt in string }

5 operator bool () const { true fuer Objekte , die als ungleich 0 interpretiert werden }

6 bool operator ! () const { true fuer Objekte , die als gleich 0 interpretiert werden }

7 }

2.11. Vergleichsoperatoren. < > <= >=

2.12. Äquivalenzoperatoren. == !=

2.13. Wertveränderungen. += -=. . .

Page 39: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

2. OPERATORFUNKTIONEN 39

2.14. Kommaoperator. , a, b res X::operator, (T rs) {} res operator,(X x, T rs)

Wichtig: Vor C++17 gilt der Kommaoperator als kommutativ; d.h. es ist nicht garantiert, dass die linke Seite vorder rechten ausgewertet wird.

In älteren Programmen wird ein Kommaausdruck serialisiert durch

a, void(), b

void() ist explizit erlaubt (ISO 14882 5.2.3) und castet "nichts" in void.

2.15. Indexoperator. operator[]

1 class X

2 {

3 X const & operator [] const (size_t i)

4 { liefert Wert dessen , was als i-te Komponente des Objekts interpretiert werden kann. }

5 X & operator [] (size_t i)

6 { liefert Referenz auf i-te Komponente des Objekts }

7 };

2.16. Funktionsoperator. operator()

1 class X

2 {

3 returntype operator ()

4 { frei programmierbare Leistung wenn ein Objekt als Funktion aufgerufen wird }

5 returntype operator(int i)

6 { ueberladbar }

7 };

8

9 // Aufruf:

10

11 X x;

12 x ();

13 x (4);

2.17. Adressoperator. operator&

2.18. Dereferenzoperator. operator*

2.19. Struktur-Dereferenzoperator. operator->

2.20. Pointer-to-member Operator. operator->*

Exkurs: Zeiger auf Funktionen: C++ beherrscht wie C Zeiger auf Funktionen. Das sind normale Zeiger, die Adressen enthaltenund deren Dereferenzierung eine Funktion mit einer de�nierten Signatur ist. Sie sind nützlich als Argumente von Funktionen,die andere Funktionen benutzen.

1 double f (double x) { return x*x; } // square

2 double g (double x) { return 1/x; } // reciprocal

3 double h (double x) { return sin (x); } // intrinsics may have no address

4

5 double integral (double (*f) (double), double a, double b, int n)

6 // *f is real function

7 // f is pointer to real function

8 { // trapezoidal rule (quick and poorly implemented)

9 double s, h, xi;

10 int i;

11 h = (b-a) / n;

12 s = ((*f) (a) + (*f) (b)) / 2;

13 for (int i = 1; i < n; ++i)

14 {

15 xi = ((n-i)*a + i*b) / n; // = a + i*h = a + i*(b-a)/n = a + i/n*b - i/n*a

16 s += f (xi); // f (x) == (*f) (x)

Page 40: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

40 3. OBJEKTORIENTIERTE PROGRAMMIERUNG

17 }

18 return s * h;

19 }

20

21 std::cout << "Int (x^2, 0, 1) = " << integral (&f, 0, 1, 10) << std::endl;

22 std::cout << "Int (x^2, 0, 1) = " << integral (&f, 0, 1, 100) << std::endl;

23 std::cout << "Int (x^2, 0, 1) = " << integral (&f, 0, 1, 1000) << std::endl;

24 std::cout << "Int (1/x, 0, 1) = " << integral (&g, 1, 2, 1000) << std::endl;

25 std::cout << "Int (sin x, 0, 3.14) = " << integral (h, 0, 3.141592653589793 , 1000) << std::endl;

26 // &h == h

Man beachte insbesondere die explizite Erlaubnis, bei Funktionszeigern jeweils beide Schreibweisen zu verwenden: &f ≡ f

und (*f) (x) ≡ f (x).

Man beachte weiter, dass die Klammern wegen der C-Klammerregeln in (*f) (x) stehen müssen, sonst wäre das eineFunktion mit einem Zeigerergebnis und dessen Bezugsvariable.

Ähnlich gibt es in C++ Zeiger auf Member von Klassen. Zum Aufwärmen erst ein einfaches Beispiel:

1 class X

2 {

3 public:

4 int i;

5 int j;

6 int k;

7 void f () { std::cout << " f()"; }

8 void g () { std::cout << " g()"; }

9 void h () { std::cout << " h()"; }

10 };

11

12 X * p = new X;

13

14 int X::* pi;

15 p->i = 5; p->j = 6; p->k = 7;

16 pi = &X::i; cout << p->*pi << endl;

17 pi = &X::j; cout << p->*pi << endl;

18 pi = &X::k; cout << p->*pi << endl;

19 void (X::* pf) ();

20

21 pf = &X::f; (p->*pf)(); cout << endl;

22 pf = &X::g; (p->*pf)(); cout << endl;

23 pf = &X::h; (p->*pf)(); cout << endl;

p zeigt auf ein neu erzeugtes Objekt der Klasse X. pi zeigt auf ein int-member dieser Klasse - nicht etwas des neuenObjekts. pf zeigt auf eine Member-Funktion dieser Klasse mit der Signatur void () (). Die Member-Zeiger werdenjetzt konkret gefüllt und dann durch Dereferenzierung mit ->* zur Ausgabe benutzt. Mit einer direkten Variablenfunktioniert ein analoger Mechanismus auch mit dem Operator .*

Merke: Die Memberzeiger zeigen auf ein Member der Klasse. Erst in Kombination mit einem Objekt und einemder beiden Operatoren .* ->* können sie dereferenziert werden.

Und damit zu einem komplexeren Beispiel:

Ein idealer Student wechselt zwischen Lernen, Vergnügen und Schlaf. Wenn er das etwa 1000 Tage (3 Jahre)gemacht hat, ist er fertig und bekommt seinen Bachelor.

1 class Student {

2 public:

3 void learn () { cout << "learning" << endl; }

4 void party () { cout << "partying ..." << endl; }

5 void sleep () { cout << "sleeping ..." << endl; }

6 };

7

8 class Do_Call_Of_Pointer_To_Member_Function {

9 public:

Page 41: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

2. OPERATORFUNKTIONEN 41

10 typedef std::pair <Student*, void (Student ::*)()>

Object_With_Pair_Of_Student_And_Member_Function_Pointer;

11

12 Do_Call_Of_Pointer_To_Member_Function(const

Object_With_Pair_Of_Student_And_Member_Function_Pointer& p)

13 : pair_object(p) {}

14

15 void operator ()() const

16 { (pair_object.first ->* pair_object.second)(); }

17

18 private:

19 Object_With_Pair_Of_Student_And_Member_Function_Pointer pair_object;

20 };

21

22 class Student_Pointer {

23 public:

24 Student_Pointer(Student *p): ptr(p) {}

25 Student_Pointer(Student_Pointer && p) : ptr(nullptr) { swap (p.ptr , ptr); }

26 ~Student_Pointer () { delete ptr; }

27

28 Do_Call_Of_Pointer_To_Member_Function const operator ->*(void (Student ::*p)()) const

29 { return std:: make_pair(ptr , p); } // implicit constructor call

30 private:

31 Student* ptr;

32 };

33

34 int main()

35 {

36 typedef void (Student ::* pointer_to_member_function)();

37

38 Student_Pointer stud = new Student;

39

40 pointer_to_member_function activity = &Student ::learn; // freshman don't party

41 (stud ->*activity)();

42 activity = &Student :: sleep;

43 (stud ->*activity)();

44

45 pointer_to_member_function pa [3]; // older students do

46 pa [0] = &Student ::learn;

47 pa [1] = &Student ::party;

48 pa [2] = &Student ::sleep;

49 for (int i = 0; i < 20; ++i) (stud ->*pa[i%3])();

50 }

2.21. Arrow-Operator. ->

2.22. Speicherverwaltung. new new[] delete delete[]

2.23. Programmiererliterale. Erste Frage: Wer hat die denn gerufen? Zweite Frage: Wer liebt sie und werbenutzt sie? Dritte Frage: Wer hasst sie?

Ein kontroverses neues Feature von C++ mit Haken und Ösen und vielen positiven Beispielen, die sich (meinerMeinung nach) erst im Gebrauch als negativ herausstellen werden, weil eine weitere klare Sprachstruktur verdunkeltwird.

Seit 2011 kann man für ganzzahlige und reelle Literale, für Zeichenliterale und für Stringliterale ein beliebiges Su�xverwenden. Das geht selbstverständlich nicht für Variable (a + b_i).

Eigene Su�xe müssen mit einem Unterstrich beginnen; Su�xe ohne Unterstrich sind dem Standard vorbehalten.Diese Regel ist genau gegenteilig zu der bei Namen (identi�ern). Hier sind die mit Unterstrich beginnenden Namenfür den Standard reserviert.

Page 42: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

42 3. OBJEKTORIENTIERTE PROGRAMMIERUNG

Die Bedeutung der folgenden Beispiele ist suggestiv, jedoch hier nicht nicht festgelegt. Suggestionen können kata-strophale Irrlichter sein!

1 double x = 1.3 _miles;

2 x = 2.0_km;

3 if (1_kg == 2.2046226 _lb)

4 std::complex <double > z = 1 + 2_i;

5 Point p = 25_x + 34_y + 3_z;

6 Date d1 = 20170731 _ad;

7 Date d2 = 15821005 _ad;

8 Date d3 = 10101bc;

9 Date d4 = "1954 -06 -07"_ad;

10 Date d5 = "12/24/2017"_ad;

11 font_size fs = 12_pt;

12 double angle = 60 _degrees;

Verwendete Su�xe müssen als Operatorfunktion de�niert werden. Als Argumente sind unsigned long long int,long double, char, wchar_t, char16_t, char32_t, const char *, const wchar_t *, const char16_t * undconst char32_t *, zugelassen.

Die Operatorfunktion wird mit

returntype operator "" identifier (argument) { conversion code }

de�niert.1 long double operator "" _miles (long double x) { return x * 1.609; }

2 long double operator "" _km (long double x) { return x; }

3 long double operator "" _lb (long double x) { return x * 0.4535923; }

4 complex <long double > operator "" _i (long double x) { return complex <long double > (0, x); }

5 long long operator "" _ad (long long n) { return Date (n%100, (n/100) %100, n/10000); }

6 long long operator "" _ad (char * p) { return /* zu kompliziert */ }

7 long long operator "" _bc (long long n) { return Date (n%100, (n/100) %100, -n/10000); }

8 double operator "" _degrees (long double n) { return n / 180.0L * 3.14L; }

"a"_b res operator""_b (T a) {}

Seit 2014 können in der De�nition der Operatorfunktion auch sogenannte user-de�ned-string-literal(e) stehen -achten sie auf das fehlende Leerzeichen zwischen den Gänsefüÿchen und dem identi�er! Jetzt sind beliebige Wörter,auch Schlüsselwörter erlaubt. Die Unterstrichregel bleibt gültig, so dass das folgende Beispiel im User-code nichtauftauchen sollte.

1 float operator ""if (float x) { };

2 double operator ""i (double x) { };

3 long double operator ""i (long double x) { };

4 std:: complexfloatdouble > zf = 1 + 2if;

5 std::complex <double > z = 1 + 2i;

6 std::complex <long double > zl = 1 + 2il;

Die Sprache C++ wird mit diesem Feature nicht klarer und durchsichtiger.

In der Library gibt es neben den drei gerade genannten Schreibweisen für imaginäre Zahlen vorde�nierte Program-miererliterale im Namespace std::literals::complex_literals:

Uhrzeiten: operator ""h operator ""min operator ""s operator ""ms operator ""us operator ""ns

Character array to basic_string: operator ""s

Character array to string_view: operator ""sv

3. Beispiel: Schnitt zweier Geraden

4. Übersicht über die C++-Datentypen

Das folgende Diagramm gibt eine Übersicht über wichtige Begri�e im Zusammenhang mit C++-Datentypen undüber die Datentypen selbst. Alle Abgrenzungen lassen sich leider nicht in einem einzigen zweidimensionalen Dia-gramm darstellen.

Page 43: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

4. ÜBERSICHT ÜBER DIE C++-DATENTYPEN 43

Abbildung 2. Schnitt zweier Geraden

Der Begri� Aggregat umfasst neben Aggregat-Klassen auch Arrays. Er taucht im Diagramm zweimal auf. Aggregat-Variable können mit geschweiften Klammern und/oder Gleichheitszeichen initialisiert werden.

struct Aggregat {

int i;

double x;

};

Aggregat a = { 2, 3.14, };

Die De�nition einer Aggregat-Klassen verbietet neben eigenen Konstruktoren, privaten Daten-Membern, Basis-Klassen und virtuellen Funktionen vor allem die Initialisierung von Membervariablen:

struct KeinAggregat {

int i = 2;

std::vector<int> s {2, 3, 5};

};

Im Diagramm fehlen die Begri�e

• Trivially copyable type: Alle skalaren Typen zusammen mit trivially copyable classes• Trivial type: Alle skalaren Typen zusammen mit trivial classes• Standard-layout type: Alle skalaren Typen zusammen mit standard-layout classes• POD-type: Alle skalaren Typen zusammen mit POD classes• Literal type: Alle skalaren Typen, Referenzen, Arrays und Klassen

Trivial kopierbar sind alle Typen, deren Variable byteweise in ein char-Array kopiert und von dort zurückkopiertwerden können, ohne dass der Inhalt korrumpiert wird.

Triviale Typen können statisch initialisiert werden.

Standard-layout bezeichnet die Eigenschaft einer festgelegten Darstellung in einer Bytefolge, die sogar kompatibelzu C ist.

Das Akronym POD steht für plain old data und bezeichnet Typen mit beiden Eigenschaften, trivial und standard-layout.

Page 44: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

44 3. OBJEKTORIENTIERTE PROGRAMMIERUNG

Der Standard benutzt die Begri�e class, struct und union als Oberbegri�e und als konkrete Typenbezeichnungenclass, struct und union. class als Typenbezeichnung ist eine Klasse, die mit privaten membern beginnt. structbeginnt mit public membern. union ist eine Klasse mit alternativen membern, von denen zu einem Zeitpunkt nureines gespeichert sein kann.

class als Oberbegri� bezeichnet alle drei Typen class, struct und union. struct als Oberbegri� bezeichnet classund struct. union als Oberbegri� bezeichnet nur union.

(Diese Darstellung ist gegenüber dem Standardtext stark vereinfacht und versucht die wesentlichen Punkte vorzu-stellen.)

5. Beispiele

5.1. Datum und Uhrzeit; Zeitachse.

5.2. Punktmenge.

Page 45: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

5. BEISPIELE 45

C++-Datentypen

fundamental types

void

object types

scalar types

arithmetic types

integer types (integral types)bool

standard integer

signed integer unsigned integer

standard signed integer standard unsigned integer

narrow character types (basic character set)(plain) char signed char unsigned char

wchar_t short (int) unsigned short (int)char16_t→

↘(plain) int unsigned (int)

char32_t long (int) unsigned long (int)long long (int) unsigned long long (int)

extended integerextended signed integer extended unsigned integer

�oating point types

�oatdoublelong double

compound types

std::nullptr_t ∗ pointer type (Zeiger) X::∗ pointer to member enumeration typeenumenum classenum struct

class struct union classes

general classestrivially copyable classes

trivial classes

standard-layout classes

aggregates[ ] array type

aggregates

POD classesPOD struct POD union

function type reference type& l-value ref.&& r-value ref.

Abbildung 3. Übersicht über die C++-Datentypen

Page 46: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem
Page 47: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

KAPITEL 4

Polymorphie (Vererbung - inheritance)

1. Vererbung

Skizze

Vererbung Inheritance Polymorphie

Erblasser Basisklasse Superklasse

Erbe abgeleitete Klasse (derived), Subklasse Erweiterung (extension)

Konzept

Gleichnamige Variable: shadowing/ausgrauen

Gleichnamige Funktionen: "

Virtuelle Funktionen: Überschreiben

Notwendige Bedingungen der Überschreibung: Zeiger + gleichnamig + virtuell

Anwendung: Shape

2. Abstrakte Klassen

Abstrakte Klassen generieren keine Objekte (können nicht instanziiert werden). Ihr einziger Zweck ist die Benutzungals Superklasse.

Eine pure virtual function ist eine speziell gekennzeichnete Funktion ohne Implementierung (ohne Code).

virtual void f () = 0;

Eine Klasse ist abstrakt, wenn sie mindestens eine pvf enthält.

Eine (direkte oder indirekte) Subklasse einer abstrakten Klasse ist konkret, wenn alle geerbten pvf überschriebensind.

pvf de�nieren also Interface-Teile (Funktionssignaturen).

Abstrakte Klassen dürfen Daten und Nicht-pvf für deren Verarbeitung enthalten.

Dann brauchen sie ctor, dtor, Big Five, Operatoren.

3. Mehrfachvererbung

class X: A, B

virtual

4. Interfaces

Interface: Klasse mit nur pvf. Sie enthalten nur Funktionssignaturen und stellen ein reines Interface dar. Sie de�-nieren Schnittstellen für klar umgrenzte Zwecke.

ISP: Interface segregation principle: Zu groÿe Interfaces werden so zerlegt, dass eine implementierende Klassejeden Teil unabhängig verwenden kann. Die implementierenden Klassen müssen dann nur noch die Funktionenüberschreiben, die sie wirklich benötigen. Ziel: Schlanke Interfaces.

Gekoppelte Systeme werden so entkoppelt.

47

Page 48: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

48 4. POLYMORPHIE (VERERBUNG - INHERITANCE)

Abbildung 1. Polymorpie in C++

Beispiel: Geometrisches Objekt mit Interface zur Position in Ebene, auf Bildschirm, Fläche, Darstellungsfarbe. . .

class geo { point getPos () =0; screenpoint getSPos () = 0; ... };

Page 49: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

5. SPEZIALTHEMEN 49

Schon zum Testen der Rechnungen muss man auch die Zeichenfunktionen überschreiben.

Bessere Lösung:

class computable { point getPos () =0; ... };

class drawable { screenpoint getSPos () = 0; ... };

class geo: computable, drawable { ... }

class testcompute: computable { ... }

class testdraw: drawable { ... }

5. Spezialthemen

Objekterzeugung: globale Objekte (auÿerhalb von geschweiften Klammern im globalen Speicher) Wer/wann ruftden Konstruktor?)

X x (3) auÿerhalb von {}

Felder von Objekten:

X x [n] nur default ctor möglich

X *p = new X* [n]; for (i = 0; i < n; ++i) p[i] = new X(i);

globale Objekte

direkte Objekte im Stack (Konstruktoren X x;)

indirekte Objekte im Heap (Konstruktoren X = new X();)

Factories: Objekterzeugende Funktionen

class X { X(){}; public: X * getX () { return new X(); } };

Private Konstruktoren?

Virtuelle Destruktoren??? ja!!!

Virtuelle Konstruktoren?? ctor immer Klassenbezogen!!!

Vererbung globaler Funktionen???

Vererbung der wichtigen Inserter und Extractor Operatoren???

Modern C++ Design, by Andei Alexandrescu

Page 50: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem
Page 51: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

KAPITEL 5

Entwurfsmuster (Design patterns)

1. UML

2. Framework

3. Strategy

4. Singleton

5. Factory

6. Klassendesign für das Newton-Verfahren

51

Page 52: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem
Page 53: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

KAPITEL 6

Generische Programmierung (Templates)

Allgemein sind Templates in C++ Entities mit variablen Typen, die durch Typparameter spezi�ziert werden.

1. De�nition von Template-Variablen

Template-Variable existieren seit C++14. Sie de�nieren eine zusammengörende Menge von Variablen verschiedenenTyps.

template<parlist>var-declaration

Als Beispiel wird die Zahl π in verschiedenen Typen erzeugt. Die angegebene Genauigkeit reicht auch aus, wennlong double eine IEEE-754 quad-zahl ist.

template<typename T>constexpr T pi = T(3.1415926535897932384626433832795028841971L);

Instanziierungen erfolgen erst, wenn die Template-Variable benutzt wird:

double x = pi<double>;

complex<long double> z = pi<complex<long double>>;

Von Template-Variablen wird hauptsächlich bei Klassenvariablen (static member) in Template-Klassen Gebrauchgemacht.

2. Funktionen

Literatur:

David Vandevoorde, Nicolai M. Josuttis: C++ Templates: The Complete Guide, 2002, Addison Wesley, 0201734842,9780201734843

http://www.icce.rug.nl/documents/cplusplus/, Frank B. Brokken, C++ Annotations Version, University ofGroningen, 9036704707, Kapitel 21, 22

2.1. De�nition von Funktionstemplates. Funktionen haben oft ähnlichen oder sogar identischen Code,auch wenn sie für verschiedene Datentypen de�niert werden.

1 int abs (int x) { return x < 0 ? -x : x; }

2 double abs (double x) { return x < 0 ? -x : x; }

3 std::complex <double > abs (std::complex <double > x) { return hypot (x.real(), x.imag());}

C++ hat für solche Fälle die Möglichkeit der Funktionsüberladung. Alle drei Funktionen können gleichzeitig ineinem Programm de�niert und ausgeführt werden. Dass die ersten beiden Funktionen sogar praktisch identisch sind,ist trotzdem ärgerlich. Mit Templates (Muster, Schablone) erhalten beide Funktionen eine gemeinsame De�niton.Dabei wird der Typ der Funktionsargumente durch einen Typparameter (hier T) ersetzt.

1 template <typename T>

2 T abs (T x) { return x < 0 ? -x : x; }

Die erste Zeile ist der Template-Header und erlaubt in spitzen Klammern ein oder mehrere Typparameter. DasSchlüsselwort typename kann auch durch class ersetzt werden; diese Möglichkeit gilt allerdings als veraltet.

1 template <typename T1, typename T2...>

Ein Funktionstemplate wird vom Compiler zunächst nur registriert. Der Compiler erzeugt noch keinen Code.Manche Fehler werden schon angezeigt, aber der folgende Fehler wird zunächst noch nicht registriert:

53

Page 54: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

54 6. GENERISCHE PROGRAMMIERUNG (TEMPLATES)

1 template <typename T>

2 T abs (T x) { return x<<1 < 0 ? -x : x; } // dieser Fehler wird noch nicht erkannt

Ein Template wird erst aktiv, wenn die dadurch de�nierte Funktion benutzt wird. Das kann durch Aufruf derFunktion (oder auch durch Ermittlung ihrer Adresse mit &) geschehen.

1 std::cout << abs (-3) << "\n";

2 std::cout << abs ( -3.14) << "\n";

Beim ersten Aufruf wird die Funktion abs für den Typ T =int erstellt, beim zweiten Aufruf ist T =double. Es istjeder Typ zugelassen, der einen Vergleich < 0 und eine Prä�x-Negation −x erlaubt, auch Typen selbstgeschriebenerKlassen. In der obigen fehlerhaften Variante würde der Compiler erst bei der Erzeugung der Funktion für T =doubleeinen Fehler melden; der Schiebeoperator ist hier nicht erlaubt.

template<typename T, typename T1...>

template<class T>

Funktion mit T, T1 in den Argumenten (nicht im Ergebnis).

2.2. Template Typparameter. T const & x

T darf im Ergebnis und im Rumpf auftauchen.

Alle Operationen im Rumpf müssen für T erlaubt sein.

2.3. Instanziierung. erst Funktionsaufrufe generieren Template-Code (Instanziierung):

template<typename T> T abs(T x) { return x < 0 ? -x : x; }

x = abs (-5); y = abs (-3.14); s = abs ("Hans");

Das letzte Beispiel geht nicht, da in std::string kein operator-() existiert.

template<typename T> T abs(T const & x) { return x < 0 ? -x : x; }

Vorteile/Nachteile?

2.4. Template Return-Typen. template<typename T1, typename T1> T add(T1 const & x, T2 const & y) { return x + y; }

T= T1, T2???

template<typename T1, typename T1> decltype(x+y) add(T1 const & x, T2 const & y) { return x + y; }

template<typename T1, typename T1> auto add(T1 const & x, T2 const & y) -> decltype (x + y);

2.5. Code bloat mit HeaderDateien. Templates in Header → Compiler langsam:

1 #include <cmath >

2 #include <algorithm >

3 #include <string >

4 #include <iostream >

5

6 int main()

7 {

8 }

1 time g++ x.cpp

2 real 0m5.622s alle 4

3 real 0m0.674s "

4 real 0m0.704s "

5 real 0m0.300s keine

6 real 0m0.341s cmath

7 real 0m0.399s algorithm

8 real 0m0.444s string

9 real 0m0.604s iostream

10

11 time clang ++ x.cpp

Page 55: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

3. KLASSEN 55

12 real 0m1.250s alle vier

13 real 0m1.218s

14 real 0m0.476s keine

15 real 0m0.492s

16 real 0m0.576s cmath

17 real 0m0.729s

18 real 0m0.862s algorithm

19 real 0m0.670s

20 real 0m0.744s string

21 real 0m0.742s

22 real 0m1.057s iostream

23 real 0m0.994s

Instanziierungen von Templates bei Funktionsaufrufen.

Mehrfachinstanziierungen derselben Instanz werden vom Linker entsorgt. → linker langsam

Sowenig wie möglich header�les. So kleine wie möglich Header�les.

iosfwd statt iostream

2.6. Deklaration von Funktionstemplates. template<typename T> T abs(T x);

2.7. Deklaration von Instanziierungen. template int abs<int>(int x);

template double abs<double>(double x);

2.8. Instanziierungen. Explizite Instanziierung von Templates

abs<int>

Instanziierung, wenn Funktion augerufen wird.

wenn Adressen der Funktion gebildet werden.

(Stelle ist Instanziierungspunkt)

Problem:

int f (int x){} int f (int x, int y = 3){} template<typename T> T f (T x){} f(3)???

ambiguity

2.9. Überladung von Template-Funktionen. template<typename T> T abs(T x) { return x < 0 ? -x : x; }

template<typename T> T abs(T x, T y) { return hypot (x, y); }

x = abs (-5); y = abs (-3.14); z = abs (3.14, 2.71);

template<typename T> T abs(std::vector<T> const & v) { }

2.10. Spezialisierung von Template-Funktionen. template<> unsigned char abs(signed char c) { if (c < 0) return c + 128; return c; }

3. Klassen

3.1. De�nition von Template-Klassen. template<typename T> class X { T * p; ...};

X<int> o;

X<Y> o;

3.2. Member Templates. template<typename T> X<T>::X(T t) : i(t) { ... }

3.3. Default template parameter. template<typename T = int> class X { ... };

X<> o;

3.4. Deklaration von Template-Klassen. template<typename T> class X;

Page 56: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

56 6. GENERISCHE PROGRAMMIERUNG (TEMPLATES)

3.5. Instanziierungen verhindern. extern template class std::vector<int>;

Beschleunigt Übersetzungen

Page 57: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

KAPITEL 7

Exceptions

1. Problemsituationen in Programmen

In moderner Programmentwicklung betrachtet man immer einzelne in sich abgeschlossene Programmabschnittemit einer klar de�nierten, abgegrenzten Tätigkeit. Damit der Abschnitt erfolgreich ablaufen kann, benötigt ervorher bestimmte Voraussetzungen (preconditions). Nach dem Ablauf hat er einen neuen Zustand erzeugt, derdurch weitere Bedingungen charakterisiert ist (postconditions). Im Normalfall sind bei Beginn des Abschnitts allepreconditions erfüllt, der Abschnitt erledigt seine Arbeit und hinterläÿt erfüllte postconditions.

Sehr häu�g werden solche Abschnitte als eigene Funktionen implementiert, das ist jedoch nicht zwingend.

Hier kann man drei Problemfälle erkennen: Vor Beginn des Abschnitts sind die preconditons nicht erfüllt; das istVerantwortung des oder der zeitlich vorangehenden Abschnitte. Nach dem Abschnitt sind die postconditions nichterfüllt; dann ist ein auf ungenügende Analyse zurückzuführender Programmierfehler passiert. Der Abschnitt mussneu analysiert und programmiert werden. Noch schlimmer sind während des Abschnittes auftretende unerwarteteSituationen.

2. Reaktionsmöglichkeiten in Problemsituationen

Die (leider) häu�gste und einfachste Reaktion auf Probleme ist es, nichts zu tun. Der Programmierer setzt voraus,dass preconditions und postconditions nie verletzt werden und nichts unerwartetes geschieht. Ist das nicht der Fall,stürzen solche Programme meist mit völlig unvorhersehbaren Konsequenzen ab.

Ist ein Programmabschnitt als Funktion implementiert, dann erzeugt diese Funktion meist Resultate. Alle Problemekönnen innerhalb dieser Resultate gemeldet werden. Das geschieht in Form von speziellen Resultatwerten (= 0ist Fehler - fopen, malloc in C; ̸= 0 ist Fehlernummer - fclose in C) oder bei komplexeren Ergebnissen durchzusätzliche Fehlermember mit de�niertem Inhalt. Bei dieser Methode ist ein Problem, dass oft vergessen wird,diese Fehlersituationen abzufragen und adäquat darauf zu reagieren.

Man kann eine globale (schlecht, Namensraumverschmutzung) oder halbglobale (Namespace-Variable, static Klas-senvariable) bereitstellen, die mit dem Wert fehlerfrei initialisiert wird und bei jeder Problemsituation einen Ver-merk erhält. Das erlaubt es, auch nach längeren Abschnitten auf die angesammelten Fehler mit einer Abfrage zureagieren (errno in der C-Library). Auch hier besteht die Gefahr, dass diese Reaktion vergessen wird.

Und man kann Exceptions verwenden.

3. Exceptions

In fast allen modernen Sprachen (C++, Java, JavaScript, . . . ) hat sich in fast identischer Form ein Exception-Mechanismus durchgesetzt.

Dabei wird ein Programmabschnitt mit einer speziellen Syntax, der try-Anweisung, markiert (try { ... }). SeineFormulierung erfolgt so klas und einfach wie möglich, ohne dass Probleme das Lesen und Verstehen erschweren.Auch die Ausführung erfolgt im Normalfall genauso, wie es der Code vorschreibt.

Natürlich müssen an entsprechenden Stellen eventuelle Probleme (Verletzung von pre- und postconditions, unerwar-tete Situationen) erkannt werden. In solchen Fällen wird mit einer weiteren Spezialanweisung (throw) eine Exceptionerzeugt (man sagt geworfen). Jede erzeugte Exception unterbricht den normalen Ablauf und setzt das Programman einer de�nierten Stelle, nämlich nach der try-Anweisung fort. Jeder Code zwischen Exception-Erzeugung unddieser Stelle wird dabei übersprungen.

Am Ende der try-Anweisung kann der Programmierer Analysecode für die Problemsituationen einfügen und Re-aktionen vorsehen. Verschiedene Probleme sollten dabei unterschieden werden können. Diese Analyse erfolgt in

57

Page 58: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

58 7. EXCEPTIONS

catch-Anweisungen. Die Argumente der catch-Anweisungen erlauben die Erkennung der verschiedenen Probleme,der Rumpf die spezi�schen Reaktionen. Das kann durch verschiedene Datentype oder auch durch verschiedeneWerte erfolgen.

Um generell Speicher und andere Resourcen wieder freigeben zu können, haben machen Sprachen einen Teil (�nally),der ohne und mit Exceptions in jedem Fall durchlaufen wird. In C++ fehlt er, man muss sich anders behelfen.

In speziellen Situationen kann man entscheiden, eine Exception weiterzugeben (rethrow). Sie kann erneut in dernächstäuÿeren try-Anweisung aufgegri�en (gecatcht) werden.

Exceptions, die nie aufgegri�en werden, beenden das Programm.

4. Exception-Syntax

Erzeugen einer Exception (Werfen):

1 throw "C-String mit Typ char const *";

2 throw std:: string("C-String mit Typ char const *");

3 throw 3.14; // schmeissen Sie doch was Sie wollen!

4 throw new T ("Hallo");

5 throw domain_error("Bessel -function with wrong argument");

Der sarkastische Satz im Kommentar ist nicht wörtlich zu verstehen: Man sollte nichts werfen, was beim Erzeugen(Konstruktoraufruf) selbst eine weitere Exception werfen könnte (bad_alloc?)! Räumen Sie Ihre Exception-Klassenauf, minimieren Sie diese und verwenden Sie brauchbare std::exceptions!

Programmabschnitt mit problematischen Teilen:1 try

2 {

3 // very complicated code

4 // will call functions in several depths

5 f ();

6 }

7 catch (T t)

8 {

9 // handle exceptions (plural) with type T

10 }

11 catch (T * t)

12 {

13 // handle exceptions (plural) with pointer type T*

14 }

15 catch (T1 t)

16 {

17 // handle exceptions (plural) with type T1

18 }

19 ...

Erzeugen einer Exception (Werfen):

1 catch (T t)

2 {

3 // handle exceptions (plural) with type T

4 throw; // rethrow this exception

5 }

Die C++-Library enthält eine Reihe vorgegebener Exception-Klassen. Die Mutter aller Klassen ist dabei std::exception:1 exception

2 logic_error

3 invalid_argument

4 domain_error

5 length_error

6 out_of_range

7 future_error(C++11)

8 bad_optional_access(C++17)

Page 59: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

5. EXCEPTION-BEISPIEL 59

9 runtime_error

10 range_error

11 overflow_error

12 underflow_error

13 regex_error(C++11)

14 tx_exception(TM TS)

15 system_error(C++11)

16 ios_base :: failure(C++11)

17 filesystem :: filesystem_error(C++17)

18 bad_typeid

19 bad_cast

20 bad_any_cast(C++17)

21 bad_weak_ptr(C++11)

22 bad_function_call(C++11)

23 bad_alloc

24 bad_array_new_length(C++11)

25 bad_exception

26 ios_base :: failure(until C++11)

27 bad_variant_access(C++17)

(aus http://en.cppreference.com/w/cpp/error/exception)

Die std::exceptions werden mit einem (C-)String initialisiert, der nach dem catch mit der Funktion what ermitteltwerden kann.

1 catch (std:: invalid_argument e)

2 {

3 std::cout << "Exception " << e.what() << std::endl;

4 }

5. Exception-Beispiel

Arbeiten mit Dateien sind sehr fehlerträchtig und damit gute Beispiele für Verwendung von Exceptions. Eingabeda-teien müssen nicht existieren oder enthalten falsche Daten. Ausgabedateien kommen auf ein volles oder gesperrtes(read-only) Medium.

Im folgenden Programm werden Zahlen aus einer Liste von Dateien gelesen und deren Wurzel am Bildschirmausgegeben. Die Dateien können Textdateien (UTF-8 mit Byte-oder-mark) oder Binärdateien sein. Die BOM ist inUTF-8 eigentlich nicht gestattet, wird hier aber zur Unterscheidung verwendet. Weitere Unterscheidungen könntenUTF-16 und UTF-32 sein.

Katastrophale Fehöler beendet das Programm, leichtere Fehler werden abgefangen und setzen das Programm sinn-voll fort.

Diese Beispiel ist ein Exception-Beispiel und nicht als Muster für Unicode-Verarbeitung zu sehen. BOM in UTF-8ist falsch; für die Zahlen würde ASCII ausreichen; es fehlt UTF-16 und UTF-32; es fehlt BOM-Erkennung; es fehlteine brauchbare Binärerkennung und es fehlen nach der Erkennung weitere Plausibilitätsprüfungen während desLesens der Dateien. Glauben Sie mir: Sie wollen das alles hier nicht lesen!

1 // excpt.cpp

2 #include <cmath >

3 #include <iostream >

4 #include <fstream >

5 #include <string >

6 #include <algorithm >

7 #include <exception >

8

9 void dotext (std:: string fn) throw (std:: logic_error , std:: string)

10 {

11 std:: ifstream f (fn);

12 if (!f) throw std:: logic_error("1");

13 if (!f.is_open ()) throw std:: logic_error("2");

14 unsigned char c0=0, c1=0, c2=0;

Page 60: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

60 7. EXCEPTIONS

15 f >> c0 >> c1 >> c2;

16 if (!(c0 == 0xef && c1 == 0xbb && c2 == 0xbf)) throw std:: logic_error("3");

17 while (f)

18 {

19 double x;

20 f >> x;

21 if (x < 0) { f.close (); throw std:: string("file has wrong data"); return; }

22 std::cout << sqrt (x) << std::endl;

23 }

24 }

25

26 void dobinary (std:: string fn) throw (std:: logic_error , std:: invalid_argument)

27 {

28 std:: ifstream f (fn , std::ios::in|std::ios:: binary);

29 if (!f) throw std:: logic_error("wtf");

30 if (!f.is_open ()) throw std:: logic_error("wtf");

31 double x;

32 while (f)

33 {

34 f.read (reinterpret_cast <char*>(&x), sizeof (double));

35 if (x < 0) { f.close (); throw std:: invalid_argument ("negative value in binary"); return; }

36 std::cout << x << ": " << sqrt (x) << std::endl;

37 }

38 }

39

40 void filetype (std:: string fn)

41 throw (std:: logic_error , std::string , std:: logic_error , std:: invalid_argument , char const *)

42 {

43 std:: ifstream f (fn.c_str (), std::ios::in|std::ios:: binary);

44 if (!f) throw "file does not exist";

45 if (!f.is_open ()) throw "file does not exist";

46 char c0=0, c1=0, c2=0;

47 f.read (&c0, 1); f.read (&c1, 1); f.read (&c2 , 1);

48 f.close ();

49 if (c0 == (char)0xef && c1 == (char)0xbb && c2 == (char)0xbf)

50 {

51 dotext (fn);

52 return;

53 }

54 dobinary (fn);

55 }

56

57 int main (int argc , char** argv)

58 try

59 {

60 /* generate a binary file:

61 std:: ofstream f("ex3", std::ios::out|std::ios:: binary);

62 double x;

63 x = 2; f.write (reinterpret_cast <char*> (&x), sizeof x);

64 x = 5; f.write (reinterpret_cast <char*> (&x), sizeof x);

65 x = -5; f.write (reinterpret_cast <char*> (&x), sizeof x);

66 x = 7; f.write (reinterpret_cast <char*> (&x), sizeof x);

67 f.close ();

68 */

69 for (int i = 1; i < argc; ++i)

70 {

71 try

72 {

73 filetype (std:: string(argv [i]));

74 }

75 catch (std:: string e)

76 {

Page 61: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

6. ASSERT 61

77 std::cout << "std:string: file " << argv [i] << "; something wrong; " << e << std::endl;

78 }

79 catch (char const * e)

80 {

81 std::cout << "char*: file " << argv [i] << "; something wrong; " << e << std::endl;

82 }

83 catch (std:: invalid_argument e)

84 {

85 std::cout << "x<0: " << argv [i] << "; " << e.what() << std::endl;

86 }

87 }

88 return 0;

89 }

90 catch (std:: exception e)

91 {

92 std::cout << "Abnormal end with " << e.what() << std::endl;

93 return 1;

94 }

Das Programm wurde mit den Testdateien gstartet und liefert nach der Übersetzung

./excpt.exe ex1.txt ex2.txt ex33 ex1.txt ex3 > excpt.res

das folgende Ergebnis:

1 4.79583

2 6

3 3

4 3

5 2

6 3

7 4

8 5

9 6

10 std:string: file ex2.txt; something wrong; file has wrong data

11 char*: file ex33; something wrong; file does not exist

12 4.79583

13 6

14 3

15 3

16 2: 1.41421

17 5: 2.23607

18 x<0: ex3; negative value in binary

Bemerkungen:

1. Eigentlich sollte man keine Strings als Exceptions verwenden. Wenn man das trotzdem tut, müssen String-Literaleals char const * gecatcht werden.

2. Will man C++-Strings verwenden, braucht man einen std::string-Konstruktor.

3. Besser sind die in <stdexcept> de�nierten Exception-Klassen.

4. Für weitere Unterscheidungen kann man in den Exception-Objekten String-Informationen unterbringen und/oderselbstde�nierte Subklassen verwenden.

6. assert

Genauso wie in C existiert in C++ die assert-Anweisung. Sie ist abschaltbar (#define NDEBUG vor #include<cassert>)und dient zum Testen während der Programmentwicklung, jedoch niemals zur Fehlererkennung.

Bedingungen, die man während der Programmentwicklung sicherstellen will, kann man mit assert(bedingung)prüfen. Das Programm bricht bei nichterfüllter Bedingung ab und der Entwickler kann die Ursache suchen. NachAbschalten wird diese Bedingung nicht mehr geprüft.

Page 62: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

62 7. EXCEPTIONS

Preconditions kann man mit assert testen, wenn durch den Programmablauf sichergestellt ist, dass die preconditionimmer erfüllt ist.

Die Existenz von Dateien sollte niemals mit assert geprüft werden, da nach Abschalten des assert eine unklareSituation über den Dateizustand vorliegt.

Page 63: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

KAPITEL 8

Reguläre Ausdrücke

Einerseits sind reguläre Ausdrücke (regex) ein bedeutendes Element der Textverarbeitung. In C und C++ jedochwurden sie schon immer sehr nachrangig behandelt.

Wegen ihrer Unverzichtbarkeit gibt es externe Libraries, die reguläre Ausdrücke auch für C und C++ bereitstellen.Wie wir noch sehen werden, haben sie nichts von ihrer Wichtigkeit verloren.

Hochwertige reguläre Ausdrücke mit Unicode-Unterstützung �nden sich in der ICU:http://site.icu-project.org/

http://userguide.icu-project.org/strings/regul\"are Ausdr\"uckep

Die C++-Library boost enthält eine Implementierung der Posix-reguläre Ausdrücke:http://www.boost.org/doc/libs/1_61_0/libs/regul\"are Ausdr\"ucke/doc/html/index.html

Wer Perlen vorzieht, kann auch die PCRE verwenden:http://www.pcre.org/

Die gute Nachricht ist, dass C++ seit 2011 reguläre Ausdrücke in der STL enthält. Sie sind sogar verwendbar � wennIhre Ansprüche nicht allzu hoch sind. Schlechter klingt die Botschaft, dass hier fast alles, was den Unicode betri�t,falsch gemacht wurde, auch wenn stolz gleich sechs reguläre Ausdrucksvarianten vorgeschrieben sind, darunter dievon ECMAScript. Das Problem beginnt damit, dass als Spezialisierungen nur char- und wchar_t-Strings de�niertsind und char16_t- und char32_t-Strings fehlen. In char kann man zwar UTF-8 speichern, die reguläre Ausdrückeanalysieren dann jedoch nur Codeunits und keine Schriftzeichen. wchar_t ist compilerabhängig und ebenfalls fürportable Programme ungeeignet. gcc speichert in wchar_t UTF-32 und ist inkompatibel zu Microsoft mit UTF-16.

Kurz: C++ enthält seit C++11 eine reguläre Ausdrucks-Library, die mit hohem Qualitätsstandard auf dem Standdes Jahres 1988 reguläre Ausdrücke de�niert, die nicht Unicode-aware sind.

#include<regul\"are Ausdr\"ucke>

1. Wahlmöglichkeiten

Die folgenden Wahlmöglichkeiten suggerieren fälschlicherweise, dass C++ kompatibel zu den jeweiligen Vorbildernist. Dem ist nicht so, C++ ist bestenfalls jeweils ähnlich. Eigentlich ist es eine gute Sache, wenn sich ein Standardin Details auf andere Standards bezieht, die dieses Detail schon ausreichend de�niert haben (Beispiel: reelle Zahlensind in IEEE-754 de�niert). C++ bezieht sich jedoch im Jahr 2011 auf den Posix-Standard, der 1985 entstandenist und damit nichts vom Unicode wissen kann..

ECMAScript ist ähnlich zu JavaScript.basic ist ähnlich zu Posix BRE.extended ist ähnlich zu Posix ERE.grep ist wie Posix grep.egrep ist wie Posix egrep.awk ist wie Posix awk.

Verlinkung der Originalde�nitionen:ECMAScript:www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdfECMA-262 7th Edition / June 2016Posix:http://pubs.opengroup.org/onlinepubs/9699919799/nframe.html POSIX.1-2008 (auch IEEE Std 1003.1-2008und The Open Group Technical Standard, Base Speci�cations, Issue 7)

63

Page 64: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

64 8. REGULÄRE AUSDRÜCKE

ECMAScript: ECMA-262 7th Edition / June 2016, 21.2 RegEXP Objects, p. 409basic: Posix , 9.3 Basic Regular Expressions,http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_03

grep: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.htmlawk: http://pubs.opengroup.org/onlinepubs/009695399/utilities/awk.html

2. Objekte für reguläre Ausdrücke

Ähnlich wie bei Strings sind reguläre Ausdrücke eine Template-Klassebasic_regex<charT,regexT> mit default basic_regex<charT,regex_traits<charT> de�niert, die neben zweiStandardspezialisierungenregex ≡ basic_regex<char> undwregex ≡ basic_regex<wchar_t> weitere Spezialisierungen zuläÿt.

Die beiden weiteren Spezialisierungentypedef basic_regex<char16_t> regex16; und typedef basic_regex<char32_t> regex32; sind leicht zu er-zeugen und könnten nützlich sein. Die Namen wurden so gewählt, dass sie mit voraussichtlichen Namen kommenderStandardversionen wahrscheinlich nicht kollidieren.

Die Erzeugung eines regulären Ausdruck-Objektes erfolgt mit einem Konstruktor, der reguläre Ausdrücke ausC/C++-Strings erzeugt und weitere Parameter hat.

regex(const char* p, size_t len, flag_type f);

regex(const string & s, flag_type f);

String-Literale enthalten bei regulären Ausdrücken viele Backslashes, so dass sich hier Raw-Literale anbieten (Den-ken Sie and die zu verdoppelnden Backslashes in C- und C++!)-Strings:

regex r("\\w+"); // Wörter mit mindestens einem Zeichen

regex r(R"re(\w+)re"); // als Raw-String

char * p; p = (char*)"\\w+"; regex r(p, strlen (p)); // als C-String

regex r("\\w+", std::regex_constants::ECMAScript); // Explizit: JavaScript-Regeln

regex r("\\w+", std::regex_constants::basic); // Posix BRE

regex r("\\w+", std::regex_constants::extended); // Posix ERE

regex r("\\w+", std::regex_constants::grep); // BRE mit \n-Erweiterung

regex r("\\w+", std::regex_constants::egrep); // ERE mit \n-Erweiterung

regex r("\\w+", std::regex_constants::awk);

\n-Erweiterung: Wenn der reguläre Ausdruck den String ohne das abschlieÿende Zeilenende matcht, gilt das alsZeilentre�er.

Weitere Flags werden geodert:

regex r("\\w+", std::regex_constants::ECMAScript | std::regex_constants::icase);

regex r("\\w+", std::regex_constants::ECMAScript | std::regex_constants::nosubs);

regex r("\\w+", std::regex_constants::ECMAScript | std::regex_constants::optimize);

regex r("\\w+", std::regex_constants::ECMAScript | std::regex_constants::collate);

3. Verwendung reguärer Ausdrücke

Typische Anwendungen regulärer Ausdrücke sind einfache und globale Tre�ersuche und Ersetzungen.

Einfache Tre�ersuche erfolgt mit std::regex_match() und std::regex_search() . Die Tre�er werden in geeig-neten Spezialisierungen der Tre�erklassen gespeichert.

In C++ wird zwischen den regex-Spezialisierungen und den C/C++-Strings unterschieden, so dass sich jeweils vierTre�erklassen ergeben:

std::cmatch

std::smatch

Page 65: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

4. ICU 65

std::wcmatch

std::wsmatch

Für globale Tre�ersuchen benötigt man Iteratoren:

std::cregex_iterator

std::sregex_iterator

std::wcregex_iterator

std::wsregex_iterator

Zur Ersetzung dient die Funktion std::regex_replace().

4. ICU

ICU bietet das beste aus der Perl-, Java- und Unicode-Welt.

http://site.icu-project.org/

http://userguide.icu-project.org/strings/regul\"are Ausdr\"uckep

1 // regexicu.cpp

2 // source: http :// userguide.icu -project.org/strings/regexp

3 // debian compile (pc55556):

4 // g++ regexicu.cpp `icu -config --ldflags `

5 // g++ regexicu.cpp -licudata -licui18n -licuio -licule -liculx -licutest -licutu -licuuc

6 // corrected: find without arguments!

7 // results:

8 /*

9 The result of find & replace is "Here_is_some_text ."

10 The number of replacements is 3

11 */

12

13 #include <iostream >

14 #include "unicode/regex.h"

15

16 int

17 main ()

18 {

19 UErrorCode status = U_ZERO_ERROR;

20 RegexMatcher m (UnicodeString (" +"), 0, status);

21 UnicodeString text ("Here is some text.");

22 m.reset (text);

23

24 UnicodeString result;

25 UnicodeString replacement ("_");

26 int replacement_count = 0;

27

28 while (m.find (/* status */) && U_SUCCESS (status))

29 {

30 m.appendReplacement (result , replacement , status);

31 replacement_count ++;

32 }

33 m.appendTail (result);

34

35 char result_buf [100];

36 result.extract (0, result.length (), result_buf , sizeof (result_buf));

37 std::cout << "The result of find & replace is \"" << result_buf << "\"" << std::endl;

38 std::cout << "The number of replacements is " << replacement_count << std::endl;

39 }

Page 66: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem
Page 67: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

KAPITEL 9

Problemfälle von C++

1. Konstante wie π

Bruce Dawson diskutiert dieses Problem in:

https://randomascii.wordpress.com/2014/06/26/please-calculate-this-circles-circumference/

Ein schon seit 1969 existierendes Problem in C ist, dass es keine De�nition von π gibt. Der Wert M_PI ist nicht imStandard festgelegt. Auch ein vor der Header-De�nition #include<cmath> eingefügtes #define _USE_MATH_DEFINES

ist kein korrektes Programm.

Die ältesten Lösungen sind, die Zahl ins Programm zu schreiben

return d * 3.14159265358979323846L

oder eine de�ne-Direktive

#define pi 3.14159265358979323846L

In beiden Fällen spezi�ziert das Su�x L eine long double, um π mit maximaler Genauigkeit zu speichern. Nachteildieser Lösung sind die Konversionen auf den Zieltyp (�oat oder double). Mehrfache Konstanten pif, pi und pil

sind eigentlich in einer Sprache mit overloadad functions nicht mehr zeitgemäÿ. Und viele Compiler erzeugen beijeder Konversion eine Warnung vor verlorenen Stellen.

Weiter verschmutzen de�ne-Direktiven den Namensraum. Wie immer man auch π benennt, es kann in groÿenProgrammen mit dort vorhandenen Namen kollidieren und deren Übersetzung stören. de�ne-Direktiven könnennicht in Namespaces gekapselt werden.

Die o�ensichtliche Lösung in C++ ist

long double const pi = 3.14159265358979323846L

in einer Header-Datei. Überraschenderweise wird jetzt bei jedem include eine eigene (konstante) Variable für πerzeugt. Bei einer ganzzahligen Konstanten passiert dies noch überraschender nicht!

Seit 2011 gibt es constexpr, die der Compiler auswertet:

constexpr long double pi = 3.14159265358979323846L;

Wer für gute (!) Programme keinen Aufwand (!) scheut, kann π extern anmontieren:

mathconst.h:extern long double const pi;

mathconst.cpp:extern long double const pi = 3.14159265358979323846L

Jetzt wird wie bei ganzzahligen Konstanten nur ein Speicherplatz der Konstanten erzeugt. Allerdings kommt füreine einfache Zahl spätestens, wenn eine dynamische Bibliothek benutzt wird, der Export aus der dll oder so-Dateials Kostenfaktor ins Spiel.

Auch die folgende Lösung scheut keinen Aufwand, diesmal mit templates:1 // pi1.cpp

2 #include <iostream >

3

4 template < class T >

5 struct MathStruct

6 {

7 static long double const pil;

67

Page 68: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

68 9. PROBLEMFÄLLE VON C++

8 };

9

10 template < class T >

11 long double const MathStruct <T>::pil = 3.14159265358979323846L;

12

13 using Math = MathStruct <void >;

14

15 int main(int argc , char* argv [])

16 {

17 std::cout << Math::pil;

18 }

Man kann π auch berechnen. Das scheint bei allen Schwierigkeiten die sauberste Lösung zu sein:

long double pi () { return 4 * atanl (1); }

Nur muss jetzt jede Verwendung von π berechnet werden. 2017 benötigte die Berechnung auf einerIntel Xeon CPU E5-2680 v2 @ 2.80GHz 136 Takte (gemessen mit qd_timer.cpp aus der qd-package). Das istsehr viel Zeit für eine Zahl.

Seit C++11 kann der Compiler diese Berechnung während der Compilation erledigen:

constexpr double pif() { return 4 * atanf(1); }

constexpr double pi() { return 4 * atan(1); }

constexpr double pil() { return 4 * atanl(1); }

Da C++ Überladungen nur an den Funktionsargumenten und niemals am Funktionsresultat unterscheidet, brauchtman die drei Namensvarianten.

Ob ein Template wirklich einfacher ist, entscheiden Sie selbst:1 // pi.cpp

2 #include <iostream >

3 #include <cmath >

4

5 template <typename T>

6 T atn (T x) { return atanl (x); }

7

8 template <>

9 double atn <double > (double x) { return atan (x); }

10

11 template <>

12 float atn <float > (float x) { return atanf (x); }

13

14 template <typename T>

15 T pi () { return 4 * atn <T> (1); }

16

17 int main(int argc , char* argv [])

18 {

19 float d = 2;

20 std::cout << pi <float >();

21 std::cout << pi <double >();

22 std::cout << pi <long double >();

23 }

Seit C++14 gibt es Template-Variable:1 // templ1.cpp

2

3 #include <iostream >

4 #include <iomanip >

5 #include <string >

6 #include <complex >

7

8 template <typename T>

Page 69: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

3. NICHT GARANTIERTER DTOR-AUFRUF 69

9 constexpr T pi = T(3.1415926535897932385L);

10

11 int main ()

12 {

13 std::cout << std:: setprecision (20) << pi <float > << std::endl;

14 std::cout << std:: setprecision (20) << pi <double > << std::endl;

15 std::cout << std:: setprecision (20) << pi <long double > << std::endl;

16 }

Schlieÿlich kennt C++ ja auch noch inline:

inline long double pi() { return 3.14159265358979323846L; }

Die Boost-Library enthält nebenbei bemerkt π � auch hier mit templates:

#include <boost/math/constants.hpp> long double pi = boost::math::constants::pi<long double>();

Zusammenfassung: Viele Fragen bei einem trivialen Problem!

2. Reell und ganzzahlig

Im letzten Abschnitt wurde schon die unterschiedliche Behandlung reeller und ganzzahliger Typen angeschnit-ten. Für ganzzahlige Konstante stehen neben den oben diskutierten die folgenden drei einfachen Methoden zurVerfügung:

#define max 1000

enum typname { max=1000, };

int const max = 1000;

Die enum-Variante ist eine garantierte Compilezeit-Konstante; die const-Variante hat nicht die vorher diskutiertenNachteile der reellen Variante. Reelle enum-Werte existieren nicht.

3. Nicht garantierter dtor-Aufruf

1 void SomeMethod ()

2 {

3 ClassA *a = new ClassA;

4 SomeOtherMethod (); // it can throw an exception

5 delete a;

6 }

1 void SomeMethod ()

2 {

3 std::auto_ptr <ClassA > a(new ClassA); // deprecated , please check the text

4 SomeOtherMethod (); // it can throw an exception

5 }

1 #include <iostream >

2 #include <memory >

3

4 struct Foo {

5 Foo() { std::cout << "Foo::Foo\n"; }

6 ~Foo() { std::cout << "Foo ::~Foo\n"; }

7 void bar() { std::cout << "Foo::bar\n"; }

8 };

9

10 void f(const Foo &foo)

11 {

12 std::cout << "f(const Foo&)\n";

13 }

14

15 int main()

16 {

Page 70: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

70 9. PROBLEMFÄLLE VON C++

17 std::unique_ptr <Foo > p1(new Foo); // p1 owns Foo

18 if (p1) p1->bar();

19

20 {

21 std::unique_ptr <Foo > p2(std::move(p1)); // now p2 owns Foo

22 f(*p2);

23

24 p1 = std::move(p2); // ownership returns to p1

25 std::cout << "destroying p2...\n";

26 }

27

28 if (p1) p1->bar();

29

30 // Foo instance is destroyed when p1 goes out of scope

31 }

Page 71: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

KAPITEL 10

Überblick über die Entwicklung von C++

Laufender Status der Standardentwicklung:

https://isocpp.org/std/status

Überblick über den Stand mehrere Compiler:

http://en.cppreference.com/w/cpp/compiler_support

Laufender Status der clang++ Compilerentwicklung:

http://clang.llvm.org/cxx_status.html

https://libcxx.llvm.org/

Laufender Status der Gnu g++ Compilerentwicklung:

https://gcc.gnu.org/projects/cxx-status.html

https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html

Laufender Status Intel icpc Compilerentwicklung:

https://software.intel.com/en-us/articles/c0x-features-supported-by-intel-c-compiler

https://software.intel.com/en-us/articles/c14-features-supported-by-intel-c-compiler

Laufender Status der Visual C++Compilerentwicklung:

https://blogs.msdn.microsoft.com/vcblog/2014/11/17/c111417-features-in-vs-2015-preview/

1. ARM

2. C++98

3. C++11

4. C++14

5. C++17

71

Page 72: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem
Page 73: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

Inhalt

73

Page 74: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem
Page 75: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

Inhaltsverzeichnis

Kapitel 1. Einführung in C++ 31. Kursübersicht 32. Designer 33. Literatur und Internet 34. Geschichte 35. Ziele und Eigenschaften 46. Compiler und Tools 47. Die Beziehungskiste von C und C++ 58. Ein erster Eindruck 8

Kapitel 2. Prozedurale Programmierung 91. Struktur von C++-Programmen 92. Hello World 133. Datentypen, Variable und Werte 134. Rechenausdrücke 135. Anweisungen 136. Funktionen 137. Header 148. Namespaces 149. Referenzen 1410. Schriftzeichen, Strings und Unicode 1411. Ein- und Ausgabe 2312. Ergänzungen 2913. struct union enum 2914. Attribute 29

Kapitel 3. Objektorientierte Programmierung 311. Kapselung 312. Operatorfunktionen 313. Beispiel: Schnitt zweier Geraden 424. Übersicht über die C++-Datentypen 425. Beispiele 44

Kapitel 4. Polymorphie (Vererbung - inheritance) 471. Vererbung 472. Abstrakte Klassen 473. Mehrfachvererbung 474. Interfaces 475. Spezialthemen 49

Kapitel 5. Entwurfsmuster (Design patterns) 511. UML 512. Framework 513. Strategy 514. Singleton 515. Factory 516. Klassendesign für das Newton-Verfahren 51

Kapitel 6. Generische Programmierung (Templates) 53

75

Page 76: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

76 INHALTSVERZEICHNIS

1. De�nition von Template-Variablen 532. Funktionen 533. Klassen 55

Kapitel 7. Exceptions 571. Problemsituationen in Programmen 572. Reaktionsmöglichkeiten in Problemsituationen 573. Exceptions 574. Exception-Syntax 585. Exception-Beispiel 596. assert 61

Kapitel 8. Reguläre Ausdrücke 631. Wahlmöglichkeiten 632. Objekte für reguläre Ausdrücke 643. Verwendung reguärer Ausdrücke 644. ICU 65

Kapitel 9. Problemfälle von C++ 671. Konstante wie π 672. Reell und ganzzahlig 693. Nicht garantierter dtor-Aufruf 69

Kapitel 10. Überblick über die Entwicklung von C++ 711. ARM 712. C++98 713. C++11 714. C++14 715. C++17 71

Inhalt 73

Page 77: C - Universität · PDF fileWill: C ++11 programmieren, 2012, 978-3-8362-1732-3 ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ... 2015 ISO/IEC TR 18822:2015 C ++ The Filesystem

INHALTSVERZEICHNIS 77

Erstellt mit LATEX und TTH:

TTH-Seite:http://hutchinson.belmont.ma.us/tth/

https://www.ctan.org/texarchive/support/tth/dist