Grundlagen für das Projekt (Teil 2)

Website: Lerne ELEKTROTECHNIK und PROGRAMMIEREN
Kurs: Kurs zum Buch "Lerne Programmieren mit Projekten: C++"
Buch: Grundlagen für das Projekt (Teil 2)
Gedruckt von: Gast
Datum: Thursday, 25. April 2024, 08:26

Beschreibung



1. Worum geht es?

Herzlich willkommen zu diesem Kapitel über benutzerdefinierte Datenstrukturen und objektorientierte Programmierung in C++! Im Folgenden geht es darum, wie du eigene Datenstrukturen erstellen und nutzen kannst. Dazu werden wir uns zunächst mit Aufzählungslisten und der Verwendung von "Enums" beschäftigen. Anschließend zeigen wir dir, wie du mit Hilfe von "Structs" Daten gruppieren kannst.

Im zweiten Teil des Kapitels werden wir uns auf die objektorientierte Programmierung konzentrieren. Du wirst lernen, wie du eine Klassenstruktur erstellst, einen Konstruktor nutzt und was Vererbung bedeutet. Diese Konzepte sind wichtige Bausteine für die objektorientierte Programmierung und bilden die Grundlage für weiterführende Themen.

2. Benutzerdefinierte Datenstrukturen

In diesem Abschnitt wirst du lernen, wie du eigene Datentypen erstellst und verwenden kannst, um deine Daten besser organisieren und verwalten zu können.

Zunächst werden wir uns mit Aufzählungslisten beschäftigen, die du mit dem Schlüsselwort enum definieren kannst. Du wirst lernen, wie du enums verwenden kannst, um switch-case Anweisungen zu vereinfachen und lesbarer zu gestalten.

Danach werden wir uns mit dem Schlüsselwort struct beschäftigen, einer weiteren Möglichkeit, Daten zu gruppieren. Mit struct kannst du mehrere Daten unter einem gemeinsamen Namen organisieren und als einheitliches Datenelement verwenden.

2.1. Aufzählungslisten definieren mit enums

Worum geht es? 

Dieses Code-Beispiel zeigt die Verwendung von Aufzählungstypen in C++. Die Aufzählungstypen (enum) werden verwendet, um eine feste Menge an Konstanten zu definieren, die dann als Werte in einer Variablen verwendet werden können.

Was kannst du danach?

  • Aufzählungstypen verwenden, um Konstanten in einer einfachen und übersichtlichen Art und Weise zu definieren


#include <iostream>
using namespace std; 

// Aufzählungstypen
enum GameObjects
{
    BIRD,
    PIG,
    SLINGSHOT,
    GROUND,
    OBSTACLE
};

int main() 
{
    GameObjects obj = GROUND;
    if (obj == BIRD or obj == PIG)
        cout << "Spiel-Charakter\n";
    else
        cout << "Lebloses Objekt\n";
        
    return 0;
}

2.2. Enums mit switch-case kombinieren

Worum geht es? 

Das Code-Beispiel demonstriert die kombinierte Verwendung von Aufzählungstypen und Switch-Case in C++. In Kombination ermöglichen Aufzählungstypen und switch-case eine klarere und effizientere Handhabung von konstanten Werten und reduzieren die Wahrscheinlichkeit von Fehlern.

Was kannst du danach?

  • Aufzählungstypen als Konstanten in einer switch-case-Anweisung benutzen


#include <iostream>

// Angepasste Aufzählungstypen
enum TransportType
{
    FOOT = 5,
    BIKE = 20,
    CAR = 100,
    PLANE = 900
};

using namespace std; 

int main() 
{
    // Aufzählungstypen und Switch-Case
    TransportType transport = CAR;
    switch (transport)
    {
    case FOOT:
        cout << "v_Fußgänger = " << FOOT << "km/h\n";
        break;
    case BIKE:
        cout << "v_Fahrrad = " << BIKE << "km/h\n";
        //break;
    case CAR:
        cout << "v_Auto = " << CAR << "km/h\n";
        break;
    case PLANE:
        cout << "v_Flugzeug = " << PLANE << "km/h\n";
        break;
    default: 
        cout << "Ungültiges Element!\n";
    }
    
    return 0;
}

2.3. Daten gruppieren mit struct

Worum geht es? 

Das Code-Beispiel zeigt, wie man eigene Datenstrukturen in C++ definieren und verwenden kann. In diesem Fall wird die Datenstruktur "GameCharacter" definiert, die die Attribute Position (x,y), Punktzahl (score) und Name (name) enthält. 

Was kannst du danach?

  • Datenstrukturen in C++ definieren
  • Objekte einer benutzerdefinierten Datenstruktur erstellen
  • Auf Attribute einer Datenstruktur zugreifen


#include <iostream>
using namespace std; 

// Eigene Datenstruktur
struct GameCharacter
{
    double pos_x{}, pos_y{};
    int score{};
    string name{};
};

int main() 
{
    // Eigenen Datentypen zusammenstellen
    GameCharacter bird;
    bird.name = "Vogel";

    GameCharacter pig{0.0, 0.0, 500, "Schwein"};
    cout << pig.name << ":" << pig.score << " Pkte\n";

    GameCharacter characters[3] = { 
        GameCharacter{0.0, 0.0, 100, "Vogel"},
        GameCharacter{0.0, 0.0, 200, "Schwein"},
        GameCharacter{0.0, 0.0, 0, "Schleuder"}
    };
    
    for(int i=0; i<3; i++)
    {
        cout << characters[i].name << endl;
    }
    
    return 0;
}

3. Objektorientierte Programmierung

Worum geht es? 

Dieser Code zeigt ein einfaches C++-Programm, das eine Klasse "Company" definiert. Die Klasse ist ein Beispiel für die objektorientierte Programmierung in C++. Die Klasse enthält zwei Member-Variablen: "m_name" und "m_city", die den Namen und die Stadt des Unternehmens speichern. Es gibt auch eine Member-Funktion "PrintLocation", die die Informationen über den Namen und die Stadt des Unternehmens ausgibt.

Was kannst du danach?

  • Definieren einer Klasse in C++ 
  • Erstellen von Objekten einer Klasse
  • Zugriff auf Member-Variablen und -Funktionen


#include <iostream>
using namespace std; 

class Company
{
public:
    string m_name{}, m_city{};

    void PrintLocation()
    {
        cout << m_name << " liegt in " << m_city;
        cout << endl;
    }
};

int main() 
{
    // Objekt einer Klasse mit Daten und Funktionen
    Company university;
    university.m_name = "Hochschule";
    university.m_city = "Emden";
    university.PrintLocation();
    
    return 0;
}

3.1. Eine Klassenstruktur erstellen

Worum geht es? 

In diesem Codebeispiel werden drei Klassen erstellt: "Food", "Company" und "Person". Jede Klasse beschreibt einen Gegenstand oder ein Konzept, wie zum Beispiel ein Lebensmittel, ein Unternehmen oder eine Person. Die Klassen speichern Informationen über diese Dinge und können bestimmte Aktionen ausführen, die für diese Dinge relevant sind. Zum Beispiel kann eine Person ein Unternehmen besuchen, bei dem sie arbeitet, oder ein Lebensmittel essen, was ihr Gewicht beeinflusst.

Die Klassen können miteinander verknüpft werden, indem sie ineinander verwendet werden. So kann man komplexere Konzepte und Beziehungen beschreiben. Die Klassenstruktur dient dazu, Daten und Funktionalität in einer übersichtlichen und organisierten Art und Weise bereitzustellen.

Was kannst du danach?

  • Verknüpfen von Klassen durch gegenseitige Verwendung in Member-Variablen
  • Eine Klassenstruktur erstellen, um komplexere Konzepte und Beziehungen zu modellieren und zu implementieren


#include <iostream>
using namespace std; 

class Food
{
public:
    string name{};
    double weight{};
};

class Company
{
public:
    string m_name{}, m_city{};

    void PrintLocation()
    {
        cout << m_name << " liegt in " << m_city;
        cout << endl;
    }
};

class Person
{
public:
    string m_name{};
    double m_weight{};
    Company m_work{};

    void AppliesForWork(Company company)
    {
        m_work = company;
        cout << m_name << " arbeitet bei "
             << m_work.m_name << " " 
             << m_work.m_city << endl;
    }

    void Eats(Food food)
    {
        m_weight += food.weight / 1000; // g in kg
        cout << m_name << " wiegt "
             << m_weight << " kg\n";
    }
};

int main() 
{
    // Member-Funktionen aufrufen
    Company university{"Hochschule", "Emden"};
    Person andreas{"Andreas", 80.0};
    andreas.AppliesForWork(university);
    andreas.Eats(Food{"Apfel", 250});
    
    return 0;
}

3.2. Der Konstruktor

Worum geht es? 

Dieser Code zeigt ein einfaches C++-Programm, das eine Klasse "Car" definiert. Die Klasse beschreibt ein Auto und enthält die Member-Variablen "m_brand" und "m_model", die die Marke und das Modell des Autos speichern.

Ein wichtiger Teil dieser Klasse ist der Konstruktor, der mit demselben Namen wie die Klasse definiert wird und keinen Rückgabetyp hat. Der Konstruktor wird aufgerufen, wenn ein Objekt der Klasse erstellt wird, und ermöglicht es, Daten beim Erstellen des Objekts zu übergeben. In diesem Beispiel werden beim Erstellen eines Autos die Marke und das Modell als Parameter übergeben.

Was kannst du danach?

  • Einen Konstruktor definieren
  • Daten beim Erstellen eines Objekts über einen Konstruktor übergeben


#include <iostream>
using namespace std; 

class Car
{
public:
    // Konstruktor
    Car(string brand, string model)
    {
        m_brand = brand;
        m_model = model;
        cout << brand << " " << model << " gebaut\n";
    }

    string m_brand{}, m_model{};
};

int main() 
{
    // Daten mit Konstruktor übergeben
    Car passat("VW", "Passat");
    Car a5("Audi", "A5");
    // Car renault; // Fehler: Parameter erforderlich!
    
    return 0;
}

3.3. Vererbung

Worum geht es? 

Dieser Code zeigt ein einfaches Beispiel für die Verwendung der Vererbung in C++. Es definiert eine Klasse "Car", die die grundlegenden Informationen über ein Auto speichert. Dann wird eine neue Klasse "HybridCar" definiert, die von der Klasse "Car" erbt. Die Klasse "HybridCar" besitzt dieselben Member-Variablen wie die Klasse "Car", aber hat auch eine neue Member-Variable "m_cap" zur Speicherung der Batteriekapazität und eine Member-Funktion "DriveElectrically", die berechnet, wie viel Strom nach einer bestimmten Fahrtstrecke verbraucht wurde.

Durch die Vererbung kann die Klasse "HybridCar" auf alle Member-Variablen und Member-Funktionen der Klasse "Car" zugreifen und erweitern. So kann man einfach und effizient Code wiederverwenden, indem man eine neue Klasse erstellt, die von einer bestehenden Klasse erbt.

Was kannst du danach?

  • Eine Klasse von einer anderen Klasse ableiten
  • Code wiederverwenden, indem man auf Member-Variablen und Member-Funktionen einer übergeordneten Klasse zugreift


#include <iostream>
using namespace std; 

class Car
{
public:
    // Konstruktor
    Car(string brand, string model)
    {
        m_brand = brand;
        m_model = model;
        cout << brand << " " << model << " gebaut\n";
    }

    string m_brand{}, m_model{};
};

// Vererbung
class HybridCar : public Car
{
public:
    HybridCar(string brand, string model, double cap) : Car(brand, model)
    {
        cout << "Hybridauto gebaut\n";
        m_cap = cap;
    }

    void DriveElectrically(int km)
    {
        m_cap -= km * 15 / 100;
        cout << "Ladung = " << m_cap << " kwh\n";
    }

    double m_cap{}; // Batterie-Kapazität
};

int main() 
{
    HybridCar prius("Toyota", "Prius", 8.8);
    prius.DriveElectrically(50);
    
    return 0;
}