Wenn innerhalb einer Klasse, eine andere Klasse gebraucht wird. Es entsteht dadurch eine Verschachtelung aus Klassen.
class C{ . public: . struct M { . // . }; };
Wenn innerhalb einer Klasse, eine andere Klasse gebraucht wird. Es entsteht dadurch eine Verschachtelung aus Klassen.
class C{ . public: . struct M { . // . }; };
lokale Variabeln überschreiben globale 8.4
Bjarne nennt in 8.4 unterschiedlichen Geltungsbereiche von Variablen
– global
– lokal
– in Klasse allein (siehe vektor unten)
– in Namensgebeit (z. B. struct, enum, …)
– in Aussagen (z. B. innerhalb eines for)
Der Geltungsbereich hat mit dem Datentyp etwas zu tun, und auf welchem RAM-Segment der Code abgelegt wird.
extern
extern int aktuelle_Version;
Die Variable wird nur deklariert und ist überall sichtbar.
Sie kann (theoretisch) in jedem File neu definiert und mit neuen Werten zugewiesen werden, das ist jedoch gefährlich. Besser ist es, nur in 1 File die Variable zu definieren und dann für alle anderen Files nur sichtbar zu machen.
Klasse
class MyVector{ . // Klassenvariable. Gültigkeit in der Klasse . vector <int> v . . public: . // öffentliche Funktion . int largest(){ . int max = 0; . for (int i = 0; i < v.size(); i++) { . max = max( x, abs(v[i]) ); . return max; . } . // öffentliche Variable . int i; . };
Der Vektor v ist nur in der Klasse sichtbar. Ausserhalb exstiert er nicht und kann auch nicht angesprochen werden.
Argumente sind lokal
int funcition_b (int value_1){ . . // }
Die Funktion ist global, aber der Parameter value_1 ist lokal bezüglich der function_b .
constexpr <function>
Lokale Variablen liegen FIFO im Stack. Man kann nicht auf sie zugreifen (Bjarne 8.5.9). Soll eine Funktion während dem Kompilieren definiert werden, braucht sie das Schlüsselwort constexpr bei all ihren Variablen und für die Funktion.
constexpr int scale_x = 3; constexpr int scale_y = 2;
constexpr Point scale( Point P){ . . return { scale_x * P.x, scale_y * P.y }; }
Bjarne Stromkamph behandelt diesen Unterschied im Kapitel 8.2.
Deklaration
extern variable_b;
void function_a();
Deklarationen reservieren Namen. Sie reservieren keinen Platz. Sie sind für den Linker wichtig.
Die „bekannteste“ Deklaration ist die forward declaration von Funktioen oder das setzen von globalen Variablen.
Deklarationen kommen in ein Header-File.
In Deklarationen müssen die Parameter nicht zwingend angegeben werden. Man macht dies jedoch, damit der Code lesbarer ist (Bjarne 8.5.1).
Definition
Mit der Definition wird Speicherplatz alloziert. Die Variable erhält einen Platz.
int var_2= 5; void function_a(){ . //code . return 0; }
Vektor
Es gibt den Datentyp Vektor in C++. Im <- Link ist einiges Hilfreiche notiert.
std::vector <type> <name>
let: Deklaration und nicht Zuweisung
Let ist ein altes Keyword und will den Vorgang der Deklaration von der Wertzuweisung unterscheiden
let value_1 = 3;
Globale Variable
extern int value_b;
Extern stehet in allen Files, die auf diese Variable zugreifen müssen.
Definiert wird die Variable nur in einem File. In diesem muss die Variable auch extern gesetzt werden.
extern ist formal eine Deklaration (und darf keinen Wert erhalten). Ermöglicht dadurch, dass die Variable in vielen Files deklariert werden kann.
const
Hat in C++ eine vielseitige Bedeutung. Const als Argument (Bjarne 8.5.4) verhindert, dass die Funktion print() die Werte versehentlich ändert.
void print( const vektor <int>& messungen){ . // code }
…
Nicht dynamisch
Definiert man eine Variable, Konstante oder ein Array mit fixer Länge char array[5] , so ist die Länge des Speicherplatzes klar und die Speicherzuweisung ist nicht dynamisch.
Nicht dynamische Speicherzuweisungen werden im STACK abgelegt.
Grund für dynamisch
Die Grösse des Arrays ändert sich während der Verarbeitung. Man kann das Array nicht auf eine fixe Grösse initialisieren.
Eine dynmische Speicherzuweisung nennt man Allozierung. Die Daten werden im HEAP abgelegt.
Vorgehen
1. Speicherplatz reservieren
Dies geschieht über die Funktion malloc(). Es ist zwingend, dass man die Anzahl Bytes nennt. int *buffer1 = malloc(n * sizeof(data));. N bezeichnet die Anzahl daten, data bezeichnet die Grösse eines Datenpackets.
Die Funktion sizeof( ) gibt immer die Anzahl Bytes der Variable zurück.
2. Auf dynamische Daten zugreiffen
Die Funktion malloc() gibt als Returnwert die Adresse zurück. Auf dynamische Daten kann nur über den Pointer zugegriffen werden. Ein direkter Zugriff über den Variablennamen auf die Daten ist nicht möglich. data1 = buffer1[0];
3. Speicherplatz freigebe
free(buffer1); Wird Speicherplatz nicht mehr freigegeben, so entsteht ein Memory Leak. Ohne free() verkleinert sich die Speicherkapazität des RAMs.
Durch das Setzen eines Asterisk vor einer Variable, definiert man, dass die Variable keinen Wert, sondern eine Adresse beinhaltet:
<type> *<name>, *<name>;
Adresse übergeben (referenzieren)
int *pointer; pointer = &variable_a;
Die Variable_a wird gelesen, aber nur ihre Adresse wird übergeben.
Wert übergeben (dereferenzieren, per value)
*pointer = 10; // oder var1 = *pointer_b; printf(" %d ", *pointer)
Der Wert wird ebenfalls über einen Stern zurückgegeben. Das ist verwirrlich, zeigt aber auch, dass es ein Wert von einem Pointer ist…
—————————————————————————–
Pointer a
int *a; int b;
Wohin zeigt a ?
b = &a; // b hat die Adresse
a soll einen Wert erhalten
*a = 10;
static lokale Variable
– Ziel:
Die Variable behält ihren Wert zwischen zwei lokalen Funktionsaufrufen.
Die Initialisierung gilt nur beim ersten Mal (Ablegen auf dem Datensegment).
– Sichtbarkeit:
Der Gültigkeitsbereich bleibt gleich. Der Ablageort der Variable ist auf dem Datensegment anstelle des Stacks.
– Bsp: Zählen von Interrupts
<type> function( ){ . static int nr_of_calls = 0; . . // do some code . nr_of_calls ++; . return <type>; }
static globale Variable
– Ziel:
Wie bei der lokalen statischen Variable. Zwischen den Aufrufen behält die Variable den Wert. Wenn sie global ist, dann behält sie den Wert während der ganzen Programmdauer.
– Sichtbarkeit:
Eine externe Variable mit static ist ausserhalb der Datei unsichtbar!
static Funktion
– Sichtbarkeit:
Wird eine Funktion extern und static gesetzt, dann ist sie ausserhalb der Datei nicht sichtbar.
Es gibt CATCH, das Testing für C++ ermöglicht. Es gibt einen Blogeintrag, der den Einsatz von CATCH auf hohem Niveau beschreibt. Zum Starten ist das Tutorial sehr gut.
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file #include "catch.hpp" unsigned int Factorial( unsigned int number ) { return number <= 1 ? number : Factorial(number-1)*number; } TEST_CASE( "Factorials are computed", "[factorial]" ) { REQUIRE( Factorial(1) == 1 ); REQUIRE( Factorial(2) == 2 ); REQUIRE( Factorial(3) == 6 ); REQUIRE( Factorial(10) == 3628800 ); }
Catch innerhalb eines Files einbauen und ausführen
1. Catch als Header in File einbinden
#define CATCH_CONFIG_RUNNER #include "catch.hpp"
2. Funktion, die getetst werden will
double calculate(string calculation) { double result = 0.0; ts.set(calculation); result = expression(); return result; }
3. Testfälle aufstellen
TEST_CASE( "r", "[digit]" ) { REQUIRE(calculate("4") == 4); REQUIRE(calculate("6") == 6); REQUIRE(calculate("4+2") == 6); REQUIRE(calculate("4+2+2") == 8); REQUIRE(calculate("4+2+2+4+2+2+1") == 17); REQUIRE(calculate("4-2") == 2); REQUIRE(calculate("8-2") == 6); REQUIRE(calculate("8-2-2") == 4); REQUIRE(calculate("3*2") == 6); REQUIRE(calculate("3*3") == 9); REQUIRE(calculate("3*2*3") == 18); REQUIRE(calculate("3+2*4") == 11); REQUIRE(calculate("3*2+4") == 10); REQUIRE(calculate("4 + 2") == 6); REQUIRE(calculate("10 + 2") == 12); REQUIRE(calculate("10 + 123") == 133); REQUIRE(calculate("-4 + 5") == 1); REQUIRE(calculate("-100 + 5") == -95); REQUIRE(calculate("(4 + 5)*2") == 18); REQUIRE(calculate("3+(-4 * 5)") == -17); REQUIRE(calculate("1.5 * 2") == 3); }
Am Ende des Files folgend die Testfälle.
4. Datei kompilieren
5. Ausführen über Konsole und Argumente mitgeben
Gibt man keine Argumente mit, werden alle Tests durchgelauften.
Struktur der Testfälle
REQUIRE(calculate("6") == 6);
Nach REQUIRE schreibt man die Funktion, die aufgerufen werden soll und gibt ihr die notwendigen Werte mit.
Nach dem == folgt das zu erwartende Ergebnis.
#include <exception>
Der Standardteil dieser Bibliothek umfasst folgende Exceptions:
Logische Fehler Runtime Fehler
– logic_error – runtime_error
– domain_error – range_error
– invalid_argument – overflow_error
– length_error – underflow_error
– out_of_range
In der Funktion
void Token_stream::put_back(Token t) { if (full) { throw std::overflow_error("buffer already full"); } full = true; buffer = t; }
class neg_sum{}; int main(){ try { . ... . . ... } catch(neg_sum const & e){ . std::cout << "negative value"<<"\n"; } }
In der Funktion
double solve_quadratic(int a, int b, int c) { double delta = pow(b, 2) - (4 * a * c); if (delta < 0) { throw Negative_delta{}; } . ..... }