Hello World!
Erstmal zur Begrüßung... Mehr...
C++ von { bis } ist eine Serie von YouTube-Videos mit Begleitmaterialien, die den Mund wässrig machen soll auf die Programmiersprache C++. Momentan ist die Serie als wöchentliche Veröffentlichung mit offenem Ende konzipiert (auch wenn das sich im Laufe der Zeit ändern kann. Auch C++ hat nur einen begrenzten Umfang…).
Erstmal zur Begrüßung... Mehr...
Wie wird aus Quellcode Maschinencode? Mehr...
Der Präprozessor ist nicht nur dazu da, Dateien zusammenzukopieren und an den Compiler zu übergeben. Er stellt noch eine ganze Menge zusätzlicher Möglichkeiten bereit, Ersetzungen im Code vorzunehmen. Mehr...
Wer immernoch nicht genug vom Präprozessor und dessen Funktionsweise hat kann sich in diesem Video mal im Detail anschauen, wie der verarbeitete Quellcode aussieht, der schließlich zum Compiler geht. Mehr...
Basisdatentypen sind nicht weiter unterteilte Datentypen: Ganzzahlen, Gleitkommazahlen, Zeichenketten etc. In etwa das, womit auf Maschinenebene gearbeitet werden kann. Mehr...
C++ erbt seine Basisdatentype von C. Da C letztlich auch bloß glorifizierter Makroassembler ist, kann das zu einigen Überraschungen führen, wenn man unvorsichtig mit den Umwandlungen zwischen den verschiedenen Typen umgeht. Mehr...
Auch wenn C++ eigentlich eine Multiparadigmen-Sprache ist (d.h. die Sprache unterstützt unterschiedliche Programmierparadigmen wie imperative oder funktionale Programmierung), so ist sie doch hauptsächlich für ihre objektorientierten Features bekannt. Grundlegend dafür ist das Konzept der Klassen. Mehr...
Später Mehr...
Später Mehr...
Später Mehr...
Später Mehr...
Später Mehr...
Später Mehr...
Später Mehr...
Später Mehr...
Später Mehr...
Später Mehr...
Bei der Festlegung von Schnittstellen kommt es häufig vor, dass man Basisklassen hat, die zwar vorgeben, wie ein Objekt auszusehen hat, aber keine sinnvolle Implementierung haben können. Um solche Klassen dann vor der Instanziierung zu schützen macht man sie abstrakt. Mehr...
Ein Thema, was vielen Programmieren die Nackenhaare aufstellt und in einigen Programmiersprachen daher nicht angeboten wird: Mehrfachvererbung. Eine Klasse erbt von zwei oder mehr Basisklassen gleichberechtigt. Ein eher seltener Anwendungsfall, aber doch nicht ganz ohne Charme. Mehr...
Die Tatsache, dass viele Programmiersprachen keine Mehrfachvererbung anbieten deutet es schon an: die Fähigkeit ist nicht ganz ohne Preis. Bei der Verwendung von Mehrfachvererbung kann man über ein Problem stolpern, welches den schönen Namen "Deadly diamond of death" – oder etwas weniger martialisch: Diamantenproblem – trägt. Konkret passiert das immer dann, wenn eine Klasse direkt oder indirekt über mehrere Pfade im Vererbungsbaum von derselben Basisklasse erbt. Mehr...
Das Diamantenproblem von letztem Mal ist leider nicht durch die virtuelle Vererbung einfach so zu lösen. Auch wenn nur noch ein Subobjekt der gemeinsamen Basisklasse in der Objekthierarchie vorhanden ist, kann es durch unterschiedliche Überschreibungen von virtuellen Funktionen zu Problemen kommen. Ein sehr ähnliches Problem entsteht auch dann, wenn es keine gemeinsame Basisklasse gibt, sondern zufällig zwei Funktionen aus unterschiedlichen Basisklassen die gleiche Signatur aufweisen. Mehr...
Eine der zentralen Fragen bei der Programmierung: wo bekomme ich Speicher her, dessen genaue Menge und Beschaffenheit ich zur Entwicklungszeit noch nicht kenne (bspw. weil das von Nutzereingaben abhängt)? Und viel interessanter (vor allem bei lang laufenden Programmen): wie werde ich den auch wieder los? Dynamisches Speichermanagement wird in der einen oder anderen Form von eigentlich allen ernstzunehmenden Programmiersprachen angeboten. In C++ ist das wie üblich etwas flexibler, dafür aber manchmal auch etwas komplizierter. Mehr...
Beim letzten Mal wurde mit dem new-Operator nur der Standardkonstruktor der betreffenden Klasse aufgerufen. Das ist in manchen Situationen etwas wenig... Mehr...
Wir wollen ja nicht immer alle Arbeit selbst machen, also bietet uns C++ eine Möglichkeit, den Compiler in bestimmten Fällen die Hauptarbeit machen zu lassen. Klassen-Templates sind genau für diese Fälle da: wir haben ein Problem, welches wir mit Hilfe einer Klasse lösen wollen, was aber für unterschiedliche Typen immer wieder gleich gelöst werden muss. Statt jedesmal eine neue Klasse zu schreiben, geben wir dem Compiler einmal eine Blaupause, aus der er sich dann bei Bedarf die passenden Klassen selbst erzeugen kann. Mehr...
Jedes Programm muss mit Fehlerbedingungen klarkommen. Die althergebrachte Weise zur Signalisierung eines Fehlers sind entsprechende Codes als Rückgabewerte von Funktionen bspw. Das ist nicht in jedem Fall sinnvoll/praktisch. Zum einen muss je nach Funktion ein Teil des Wertebereichs des Rückgabewertes für Fehlercodes abgezwackt werden und zum anderen kann eine Behandlung des Fehlers immer nur an der Stelle des Aufrufs erfolgen. C++ stellt mit Exceptions eine Möglichkeit bereit, die komplett anders funktioniert: Objekte werden als Ausnahmen "geworfen" und in entsprechenden Exception-Handlern "gefangen" um behandelt zu werden. Mehr...
Unser Optional vom letzten Mal fühlt sich irgendwie noch etwas unrund an. Wir können ihn nicht wirklich als einfachen Ersatz für den Basisdatentyp verwenden, obwohl ja eigentlich das genau das Ziel ist. Daher bauen wir das Template diesmal noch ein wenig um, damit die üblichen Dinge wie Zuweisung vom Basisdatentyp etc. funktionieren. C++ bietet mit Operatorüberladung und automatischen Konvertierungsfunktionen dafür praktische Hilfsmittel. Mehr...
Bestimmte Namen für Funktionen und Klassen bieten sich einfach an und werden daher immer wieder verwendet. Um eine Kollision zu vermeiden (speziell bei der Verwendung verschiedener Bibliotheken, die man nicht immer abändern kann und will), kann man C++-Code in verschiedene Namensräume einpacken und so Eindeutigkeit schaffen. Das Thema ist heute mal wieder auf zwei Videos aufgeteilt, da es zu viel für ein einzelnes ist. Mehr...
Beim Programmieren taucht öfter mal die Anforderung auf, Kopien von bestimmten Objekten zu verhindern. Das können bspw. große Objekte sein, die man aus Effizienzgründen nicht kopieren will oder Betriebssystemressourcen, die man nicht kopieren kann/sollte (Mutexe oder Filehandles sind da immer gute Kandidaten). Dummerweise hilft einem C++ beim Kopieren von Objekten im Normalfall, indem es die dafür notwendigen Memberfunktionen automatisch anlegt, was natürlich dann nicht mehr gewollt ist. Das kann man aber auch verhindern. Mehr...
Beim letzten Mal haben wir gesehen, wie man mittels selbst implementiertem Copy-Konstruktor das Kopieren von Objekten verbieten kann. Diesmal soll das Beispiel zeigen, wie man das Kopierverhalten eines selbstdefinierten Typs anpassen kann. Mehr...
Normale Zeiger in C++ sind ja ein wenig anstrengend zu benutzen. Wir müssen uns Speicher reservieren, immer schön auf die Zeiger aufpassen und am Ende an der richtigen Stelle wieder freigeben. Sprachen wie Java, Python oder C# bieten mit ihren Garbage Collectors da doch etwas mehr Bequemlichkeit. Man muss sich nicht ums Aufräumen kümmern, sondern lässt das vom System erledigen. C++ erkennt mit C++11 zwar zum ersten Mal an, dass man Garbage Collection betreiben kann, aber bisher gehört ein entsprechendes System nicht zum Standard. Stattdessen gibt es ab C++11 etwas anderes, was ähnliche Bequemlichkeit bietet, aber etwas besser mit den Prinzipien der Objektlebenszeit in C++ korrespondiert: Smart Pointer. Mehr...
Wenn man versucht, nur mit std::shared_ptr zu arbeiten, dass stößt man früher
oder später auf ein Problem: kreisförmige Objektbeziehungen machen unsere
schöne, neue, automatisch aufräumende Welt wieder kaputt. Referenzieren sich
zwei oder mehr Objekte im Kreis, dann können die über ihre shared_ptr selbst
dann nicht gelöscht werden, wenn sich außerhalb des Kreises keiner mehr dafür
interessiert. Um diese Kreise zu unterbrechen und trotzdem die Annehmlichkeiten
eines Smart Pointers zu haben, bietet die Standardbibliothek std::weak_ptr. Mehr...
Wie funktioniert eigentlich der std::shared_ptr hinter den Kulissen? Viel ist
(zumindest im Prinzip) nicht dazu: ein wenig Operatorüberladung, ein wenig
Zeigergeschiebe und ein Referenzzähler. Mehr...
Nicht nur Klassen können aus Templates gebaut werden, sondern auch bei Funktionen ist das möglich. Sieht im Grunde genommen wie beim Erstellen von Klassentemplates aus, generiert aber neuen Code für eine bestimmte Funktion. Dabei kann der Compiler bestimmte Templateparameter auch noch automatisch ableiten. Mehr...
Wenn's mal wieder was besonderes sein soll: Templates sind ja eigentlich als generische Möglichkeit zur Generierung von Code ausgelegt. Bei aller Verallgemeinerung hat man allerdings manchmal das Bedürfnis, für einzelne Parametertypen etwas besonderes zu machen. Dafür gibt es dann Templatespezialisierung: findet der Compiler bei der Instanziierung eines Templates eine spezialisierte Variante für einen speziellen Typ, dann nimmt er diese, statt der allgemeinen Variante. Dabei kann sich das Verhalten (oder im Falle von Klassentemplates: die Struktur) der spezialisierten Variante durchaus deutlich vom allgemeinen Template unterscheiden. Mehr...
Wir wollen – inspiriert von Ada – gern einen bereichsgeprüften Integer-Datentyp entwickeln. Der naive Ansatz einer Klasse, die die Grenzen im Objekt speichert, funktioniert, ist aber etwas ineffizient. Value Templates können uns hier eine Hilfe sein, indem sie es uns ermöglichen, Objekte mit unterschiedlichen Grenzen auch zu unterschiedlichen Typen zu machen. Mehr...
Einfach alle Objekte mit anderen Grenzen für eine Zuweisung abweisen ist nicht ganz das richtige. Eigentlich möchten wir abhängig von den Grenzen des anderen Objektes lieber eine Prüfung durchführen oder eben auch nicht. Template Metaprogramming macht genau das möglich. Mehr...
Mittels Template Metaprogramming kann der Compiler beim Übersetzen eines
Programms nicht nur zwischen verschiedenen Templatespezialisierungen
unterscheiden, sondern auch noch komplett neue Varianten eines Templates
instanziieren, wenn nötig. Das wird diesmal ausgenutzt, um eine Lücke in unserem
RangeInt-Template zu schließen: wir brauchen ja auch noch ein paar geprüfte
Rechenoperationen. Mehr...
Templatespezialisierung als Möglichkeit, absichtlich Fehler hervorzurufen – die Sprache überrascht einen doch immer wieder. Diesmal geht es um die Fähigkeit der partiellen Templatespezialisierung, bei der ein Template nur für einen Teil der möglichen Werte seiner parameter spezialisiert wird. Ansonsten bleibt der Templatetyp ohne Definition unvollständig und zwingt den Compiler zu einer Fehlermeldung, wenn eine fehlende Spezialisierung gebraucht wird. Mehr...
Iteratoren sind die flexiblen Bindeglieder der Standardbibliothek: Datenstrukturen stellen sie zur Verfügung, Algorithmen verlassen sich auf sie. Damit wir vor dem Einstieg in die Tiefen der Standardbibliothek einen Überblick kriegen, hier mal ein eher theoretisches Video über dieses Konzept. Mehr...
Als Einstieg in die C++-Standardbibliothek soll die Container-Library dienen.
Diese Sammlung von Datenstrukturen bietet für fast jeden Anwendungsfall ein
passendes Klassentemplate. Hier leuchten die Iteratoren nochmal hell auf. Sie
ermöglichen die flexible Verbindung von Containern mit Algorithmen und bieten so
eine hohe Wiederverwendung innerhalb der Bibliothek an. Wir fangen an mit
std::list und std::vector, die mittels Iteratoren mit einem kleinen
Algorithmus auf der Konsole ausgegeben werden können. Mehr...
Die beste Art von Arbeit ist ja immer die, die man nicht selbst machen muss. Daher habe ich das Beispiel vom letzten Mal umgebaut, um den Hauptanteil der Arbeit von der Standardbibliothek erledigen zu lassen. Hat eigentlich nur Vorteile: man muss selbst weniger tun und hat damit natürlich auch weniger Möglichkeiten Fehler zu machen. Mehr...
Manchmal wollen wir gern Dinge mit der Standardbibliothek verbinden, die so
erstmal nicht vorgesehen sind. Zu dem Zweck kann ein selbst implementierter
Iterator hilfreich sein. Im Beispiel hier soll ein OutputIterator die Werte,
die in ihn geschrieben werden, in eine grafische Oberfläche übertragen. Dafür
muss er natürlich die Funktionalität eines OutputIterator unterstützen und
zusätzlich noch seine Fähigkeiten der Standardbibliothek bekanntgemacht werden. Mehr...
Wie kann man eigentlich Algorithmen der Standardbibliothek noch flexibler gestalten? Eine mögliche Antwort lautet: Funktionsobjekte. C++ bietet die Möglichkeit, Objekte zu bauen, die sich aufrufen lassen wie Funktionen. Alles, was man dafür tun muss, ist die Überladung des richtigen Operators. Damit geht dann alles Mögliche: Übergabe als Parameter an andere Funktionen, Speichern von Kontext etc. Mehr...
Ein Feature, auf das viele C++-Programmierer lang gewartet haben:
lambda-Funktionen (manchmal auch: Closures) ermöglichen die schnelle und
unkomplizierte Definitionen von Funktionobjekten direkt vor Ort. Man kann sie
als Parameter für eine andere Funktion übergeben (wie in unserem Beispiel mit
std::sort), in einer Variable speichern und später aufrufen. Hervorragend
geeignet für kurze Callback-Funktionen, die bspw. zwei Objekte vergleichen. Mehr...
Die neuen Möglichkeiten für anonyme Funktionen, die C++ bietet, sind ja schön
und gut, aber: wie setze ich die eigentlich in eigenem Code ein? Der konkrete
Typ, der aus einem lambda-Ausdruck resultiert, ist ja so nicht bekannt. Zum
Glück gibt es dafür std::function: diese Template-Klasse realisiert ein
Funktionsobjekt, was sich genau auf den gewünschten Funktionstyp parametrisieren
lässt und dann so einiges an Flexibilität bietet. Mehr...
Lambda-Funktionen in C++ beherrschen einen netten Trick, der sie unglaublich viel praktischer macht, als einfache Funktionspointer: sie können Teile ihrer Umgebung "einfangen" und im Funktionsobjekt verpacken. Auf diese Art und Weise kann man Kontext, der zum Zeitpunkt der Erzeugung der Lambda-Funktion bestand, später noch verwenden. Mehr...
Was passiert eigentlich, wenn man sich nicht an die Regeln hält? Wenn man bspw. in einer Capture List einer lambda-Funktion Referenzen verwendet, die dann aus dem Scope gehen? Die Antwort lautet: keine Ahnung. Oder etwas formaler: undefined behaviour. Theoretisch darf der Compiler alles mögliche machen, praktisch wird von "einfach funktionieren" bis "durch falsche Daten Unsinn tun" alles mögliche dabei sein. Mehr...
Wie sehen lambda-Funktionen eigentlich unter der Haube aus? Das Video geht etwas theoretischer darauf ein, was der Standard für dieses Sprachmittel so anbietet und wie man das unter der Haube umsetzen könnte. Mehr...
In C++03 können einfache Datenstrukturen (vergleichbar zu C-structs) mit Initialisiererlisten initialisiert werden. Dummerweise sperrt man sich damit von den eigentlich interessanten Aspekten von C++ aus: Vererbung, Laufzeitpolymorphie, Konstruktoren... alles verboten. C++11 behebt diesen Mangel. Mit Uniform Initialization lässt sich das Prinzip auf beliebige Datentypen anwenden. Mehr...
std::initializer_list bietet uns eine bequeme Möglichkeit, die neue unified
initialization Syntax auch mit Initialisierungslisten variabler Länge in eigenen
Datentypen zu benutzen. Dabei gilt es allerdings, einige Kleinigkeiten zu
beachten. Mehr...
Wie kann die InfiniteSequence vom letzten Mal etwas konformer zur
Standardbibliothek gemacht werden? Ganze einfach: man verpasst ihr passende
Iteratoren, wie sie für die anderen Container auch vorgegeben sind. Der
Einfachheit halber sind das hier mal nur ForwardIterator. Vom Entwurf des
Containers her wären aber auch Bidirektional oder RandomAccess möglich. Mehr...
Viele Programmiersprachen bieten assoziative Container: Container, deren Index
aus beliebigen Objekten bestehen kann, statt nur auf Integer beschränkt zu sein.
C++ ist da keine Ausnahme. Genaugenommen bietet es mehrere verschiedene
Varianten für unterschiedliche Ansätze. Eine davon: std::map. Mehr...
Wie funktioniert eigentlich std::map und welche Garantien gibt der Standard?
Der Artikel zeigt einen mögliche Implementierungsansatz (der garantiert nicht
vollständig ist, da die "richtige" std::map noch viel mehr Funktionalität
unterstützen muss). Mehr...
std::map ist an sich ja schön und gut, aber man will ja oft nicht nur Strings
und Integer als Schlüssel verwenden. Eigene Typen müssen allerdings bestimmte
Anforderungen erfüllen, um hier Verwendung zu finden. Zum Glück bietet C++
mehrere Möglichkeiten, um diese Anforderungen in den unterschiedlichsten
Situationen zu erfüllen. Mehr...
Zufallszahlen in C++ müssen nicht immer der Gleichverteilung folgen. Gerade für Simulationen sind oft andere Verteilungen wesentlich wichtiger, weswegen die Standardbibliothek auch eine Sammlung der häufigsten mitbringt. Mehr...
Die Standardbibliothek bringt für fast alle Anwendungsfälle die passende Wahrscheinlichkeitsverteilung mit. Wenn es denn dann aber doch mal nicht reicht, kann man sich recht einfach eine eigene schreiben, so wie hier im Beispiel eine physikalisch "korrekte" Münze. Mehr...
constexpr sind eine Möglichkeit, ab C++ 11 (oder so richtig benutzbar ab C++
14/17) Sachen vom Compiler zur Compilezeit übersetzen zu lassen. Bisher musste
man für derartige Sachen auf Dinge wie Template Metaprogramming zurückgreifen.
Das war und ist natürlich ein mächtiges Sprachfeature, mit dem sich interessante
Probleme lösen lassen, aber für viele Sachen (und für viele Programmierer)
werden derartige Programme schnell unübersichtlich. "Klassischer" C++-Code ist
oft einfach besser lesbar, als der fast schon funktionale Stil eines rekursiven
Templates. Mehr...