Studentenverwaltung



  • Ich bin gestern über diese lustige Aufgabenstellung gestolpert:

    Übung 4
    Hochschule Bremerhaven Programmieren II Prof. Dr. Peter Kelb
    Bachelor: Informatik / Wirtschaftsinformatik
    Aufgabe 1:
    Implementieren Sie eine Klasse Student, die einen Namen, Vornamen (beides als char*), eine Matrikelnummer und das Fachsemester (beides unsigned int) enthält. Implementieren Sie eine weitere Klasse Verwaltung, die ein Array von Studenten enthält. Diese Klasse soll neue Studenten anlegen und bestehende Studenten löschen können. Sie soll das Fachsemester aller Studenten erhöhen können, Studenten bestimmter oder auch aller Fachsemester ausgeben können. Implementieren Sie noch eine Suchfunktion, so dass nach Namen oder Matrikelnummer gesucht werden kann. Wird nach Namen gesucht, müssen u.U. mehrere Studenten zurückgegeben werden. Achten Sie darauf, dass im Destruktor alle Daten wieder freigegeben werden.

    Ich finde das pervers. Wenn einer std::string verbietet, dann will er wahrscheinlich auch keine Vektoren sehen ...

    Ihr dürft gerne meine Lösung niedermachen 😉

    #include <cstdlib>     // EXIT_FAILURE
    #include <cstring>     // verartete alte in C++ unnütze Stringfunktionen, asshole. std::strlen(), std::strncpy(), std::strcmp(), ...
    #include <iostream>    // std::cin, std::cout, std::cerr
    #include <iomanip>     // std::setw()
    #include <stdexcept>   // std::runtime_error, std::invalid_argument
    #include <algorithm>   // std::swap(), std::copy(), std::find()
    #include <string>      // std::getline()
    #include <memory>      // std::make_unique<>()
    
    char const * const enomem_msg{ "Not enough memory :(" };
    char const * const invalid_arg{ "An invalid argument was supplied  :(" };
    char const * const input_error{ "Eingabefehler\n\n" };
    
    bool ignore_line(std::istream &is = std::cin)
    {
    	for (int ch{ is.get() }; ch != '\n' && ch != EOF; ch = is.get());
    	return true;
    }
    
    void reset_istream(std::istream &is = std::cin)
    {
    	is.clear();
    	ignore_line(is);
    }
    
    class Student
    {
    public:
    	static constexpr std::size_t max_name = 80;
    	static constexpr std::size_t max_vorname = 80;
    
    private:
    	friend class Verwaltung;
    
    	char *Name;
    	char *Vorname;
    	unsigned Matrikelnummer;
    	unsigned Fachsemester;
    
    public:
    	Student(char const *name = nullptr, char const *vorname = nullptr, unsigned matrikelnummer = {}, unsigned flachfernsehr = {})
    		: Name{ nullptr }, Vorname{ nullptr }, Matrikelnummer{ matrikelnummer }, Fachsemester{ flachfernsehr }
    	{
    		if (name) {
    			auto name_length{ std::strlen(name) };
    			Name = new char[name_length + 1];  // ja, kleckt wenn das nächste new[] wirft.
    			std::strcpy(Name, name);
    		}
    
    		if (vorname) {
    			auto vorname_length{ std::strlen(vorname) };
    			Vorname = new char[vorname_length + 1];
    			std::strcpy(Vorname, vorname);
    		}
    	}
    
    	Student(Student const &other)
    		: Name{ nullptr }, Vorname{ nullptr }, Matrikelnummer{ other.Matrikelnummer }, Fachsemester{ other.Fachsemester }
    	{
    		if (!other.Name || !other.Vorname)
    			throw std::invalid_argument{ invalid_arg };
    
    		auto name_length{ std::strlen(other.Name) };
    		Name = new char[name_length + 1];  // ja, kleckt wenn das nächste new[] wirft.
    		std::strcpy(Name, other.Name);
    
    		auto vorname_length{ std::strlen(other.Vorname) };
    		Vorname = new char[vorname_length + 1];
    		std::strcpy(Vorname, other.Vorname);
    	}
    
    	~Student()
    	{
    		delete[] Name;
    		delete[] Vorname;
    	}
    
    	Student& operator=(Student other)
    	{
    		swap(*this, other);
    		return *this;
    	}
    
    	friend void swap(Student &first, Student &second)
    	{
    		using std::swap;
    		swap(first.Name, second.Name);
    		swap(first.Vorname, second.Vorname);
    		swap(first.Matrikelnummer, second.Matrikelnummer);
    		swap(first.Fachsemester, second.Fachsemester);
    	}
    
    	friend
    	std::ostream& operator<<(std::ostream &os, Student const &student)
    	{
    		os << std::setw(8) << student.Matrikelnummer << ": " << student.Name << ", " << student.Vorname << " (" << student.Fachsemester << ')';
    		return os;
    	}
    
    	friend
    	std::istream& operator>>(std::istream &is, Student &student)
    	{
    		unsigned matrikelnummer;
    		char vorname[Student::max_vorname + 1];
    		char name[Student::max_name + 1];
    		unsigned fachsemester;
    
    		if ((is >> matrikelnummer) && ignore_line() && is.getline(vorname, Student::max_vorname + 1) &&
    			is.getline(name, Student::max_name + 1) && (is >> fachsemester))
    		{
    			student = { name, vorname, matrikelnummer, fachsemester };
    		}
    
    		return is;
    	}
    
    	bool operator==(Student const &other)
    	{
    		return Matrikelnummer == other.Matrikelnummer ||
    			   Name && other.Name && std::strcmp(Name, other.Name) == 0 ||
    			   Vorname && other.Vorname && std::strcmp(Vorname, other.Vorname) == 0;
    	}
    
    	bool is_valid() const
    	{
    		return Matrikelnummer;
    	}
    };
    
    class Verwaltung
    {
    	Student *Studenten = nullptr;
    	std::size_t Anzahl = 0;
    
    public:
    	Verwaltung() = default;
    
    	Verwaltung(const Verwaltung &other)
    	: Studenten{ new Student[other.Anzahl] },
    	  Anzahl{ other.Anzahl }
    	{
    		std::copy(other.Studenten, other.Studenten + other.Anzahl, Studenten);
    	}
    
    	Verwaltung& operator=(Verwaltung other)
    	{
    		swap(*this, other);
    		return *this;
    	}
    
    	friend
    	void swap(Verwaltung &first, Verwaltung &second)
    	{
    		using std::swap;
    		swap(first.Studenten, second.Studenten);
    		swap(first.Anzahl, second.Anzahl);
    	}
    
    	~Verwaltung()
    	{
    		delete[] Studenten;
    	}
    
    	friend
    	std::ostream& operator<<(std::ostream &os, Verwaltung const &verwaltung)
    	{
    		for (std::size_t i{}; i < verwaltung.Anzahl; ++i)
    			std::cout << verwaltung.Studenten[i] << '\n';
    	
    		return os;
    	}
    
    	void add(Student const &student)
    	{
    		Student *new_studenten{ new Student[Anzahl + 1] };
    		std::copy(Studenten, Studenten + Anzahl, new_studenten);
    		new_studenten[Anzahl] = student;
    		delete[] Studenten;
    		++Anzahl;
    		Studenten = new_studenten;
    	}
    
    	Student del(unsigned matrikelnummer)
    	{
    		auto it{ std::find(Studenten, Studenten + Anzahl, Student{ nullptr, nullptr, matrikelnummer, 0 }) };
    
    		if (it == Studenten + Anzahl)  // nicht gefunden du Honk
    			return Student{};
    
    		auto deleted_dtudent{ *it };
    	
    		swap(*it, Studenten[Anzahl - 1]);
    
    		Student *new_studenten{ new Student[Anzahl - 1] };
    		std::copy(Studenten, Studenten + Anzahl - 1, new_studenten);		
    		delete[] Studenten;
    		--Anzahl;
    		Studenten = new_studenten;
    
    		return deleted_dtudent;
    	}
    
    	void clear()
    	{
    		delete[] Studenten;
    		Anzahl = 0;
    	}
    
    	Student* find(unsigned matrikelnummer)
    	{
    		auto it{ std::find(Studenten, Studenten + Anzahl, Student{ nullptr, nullptr, matrikelnummer }) };
    		return it == Studenten + Anzahl ? nullptr : it;
    	}
    
    	std::unique_ptr<Student[]> find(char const *name, char const *vorname)
    	{
    		std::size_t count{};
    
    		for (std::size_t i{}; i < Anzahl; ++i)
    			if (std::strcmp(Studenten[i].Name, name) == 0 || std::strcmp(Studenten[i].Vorname, vorname) == 0)
    				++count;
    	
    		if (!count)
    			return std::make_unique<Student[]>(1);
    	
    		auto results{ std::make_unique<Student[]>(count + 1) };
    
    		for (std::size_t i{}, results_counter{}; i < Anzahl; ++i, ++results_counter)
    			if (std::strcmp(Studenten[i].Name, name) == 0 || std::strcmp(Studenten[i].Vorname, vorname) == 0)
    				results[results_counter] = Studenten[i];
    
    		return results;
    	}
    };
    
    int main()
    {
    	try {
    		Verwaltung verwaltung;
    
    		for (bool running{ true }; running;) {
    			int menu_choice;
    			while (std::cout << "[1] Anzeigen\n[2] Anlegen\n[3] Loeschen\n[4] Alle loeschen\n[5] Suche\n[6] Beenden\n\n",
    				   !(std::cin >> menu_choice) || menu_choice < 1 || 6 < menu_choice)
    			{
    				std::cerr << input_error;
    				reset_istream();
    			}
    
    			switch (menu_choice) {
    			case 1:  // Anzeigen
    				std::cout << verwaltung;
    				break;
    
    			case 2:  // Anlegen
    			{
    				Student new_student;
    				while (std::cout << "Bitte Matrikelnummer, Vor-, Zuname und Fachsemester eingeben. Jeweil bite [Enter] druecken:\n",
    					   !(std::cin >> new_student))
    				{
    					std::cerr << input_error;
    					reset_istream();
    				}
    				verwaltung.add(new_student);
    				std::cout << "\n\n" << new_student << "\n\nwurde hinzugefuegt.";
    			}
    				break;
    
    			case 3:  // Loeschen
    			{
    				unsigned matrikelnummer;
    				while (std::cout << "Bitte die zu loeschende Matrikelnummer eingeben: ",
    					   !(std::cin >> matrikelnummer))
    				{
    					std::cerr << input_error;
    					reset_istream();
    				}
    				Student deleted_student{ verwaltung.del(matrikelnummer) };
    				if (!deleted_student.is_valid())
    					std::cout << "Ein Student mit Matrikelnummer " << matrikelnummer << " wurde nicht gefunden.";
    				else std::cout << deleted_student << "\nwurde geloescht.";
    			}
    				break;
    
    			case 4:  // Alle loeschen
    				verwaltung.clear();
    				std::cout << "Alle Studenten wurden geloescht.";
    				break;
    		
    			case 5:  // Suche
    			{
    				int search_menu_choice;
    				while (std::cout << "Suche nach\n[1] Matrikelnummer\n[2] Name\n\n",
    					!(std::cin >> search_menu_choice) || search_menu_choice < 1 || search_menu_choice >> 2)
    				{
    					std::cerr << input_error;
    					reset_istream();
    				}
    
    				if (search_menu_choice == 1) {  // Matrikelnummer
    					unsigned matrikelnummer;
    					while (std::cout << "Matrikelnummer: ", !(std::cin >> matrikelnummer)) {
    						std::cerr << input_error;
    						reset_istream();
    					}
    					auto it{ verwaltung.find(matrikelnummer) };
    					if (!it) {
    						std::cout << "Ein Student mit Matrikelnummer " << matrikelnummer << " wurde leider nicht gefunden.";
    					}
    					else {
    						std::cout << *it;
    					}
    				}
    				else if (search_menu_choice == 2) {  // Name
    				
    					char vorname[Student::max_vorname + 1];
    					char name[Student::max_name + 1];
    
    					std::cout << "Bitte Vorname und Zuname durch Enter getrennt eingeben:\n";
    					ignore_line();
    					std::cin.getline(vorname, Student::max_vorname + 1);
    					std::cin.getline(name, Student::max_name + 1);
    
    					auto results{ verwaltung.find(name, vorname) };
    					if (!results.get()) {
    						std::cout << "Es wurden keine Studenten mit Namen " << vorname << ' ' << name << " gefunden.";
    					}
    					else {
    						std::cout << "Suchergebnisse:\n";
    						for (std::size_t i{}; results[i].is_valid(); ++i)
    							std::cout << results[i] << '\n';
    					}
    				}
    
    			}
    				break;
    
    			case 6:  // Beenden
    				std::cout << "Bye.";
    				running = false;
    				break;
    			}
    
    			std::cout << "\n\n";
    		}
    	}
    	catch (std::exception &e) {
    		std::cerr << e.what() << "\n\n";
    		return EXIT_FAILURE;
    	}
    }


  • Ihh, mach das weg!



  • warum?



  • Es entspricht nicht meinem ästhetischen Empfinden.


  • Gesperrt

    @Hubert-Alfonsius sagte in Studentenverwaltung:

    Es entspricht nicht meinem ästhetischen Empfinden.

    So sieht modernes C++ aus. Das musst du aushalten!



  • @Hubert-Alfonsius Geh doch mal ins Detail was dich stört und warum. Oder schreibe eine eigene Version. Vielleicht lernt man ja was von beiden Seiten.



  • @RBS2 sagte in Studentenverwaltung:

    So sieht modernes C++ aus

    nein, so sieht das nur mit bescheuerten Einschränken von einem Prof aus der schon 20 jahre nichts mehr selbst geschrieben hat.



  • @Zhavok Das, was Swordfish sagt.



  • @Zhavok sagte in Studentenverwaltung:

    Geh doch mal ins Detail was dich stört und warum.

    Bitte lerne sinnverstehend lesen. RAII und Rule of Zero ftw.



  • Hi,

    soll das jetzt C oder C++ sein?
    Die Aufgabenstellung ist eindeutig C (mit Klassen) (möglicherweise sogar noc h K&R C 😉 ).
    Meint der mit dem Wort Klasse wirklich C-Klassen oder unter Umständen nur Schulklassen?
    Der realisierte Versuch dagegen C++.
    Am besten erst mal mit dem Prof abklären was er haben will, nicht dass er nachher schon daran scheitert den Quellcode lesen zu können.

    Gruß Mümmel



  • Meiner Erfahrung nach erlaubt ein Prof der kein <string> und kein <vector> mag auch kein <stdexcept> oder <algorithm>.
    Außerdem sollte alles vermieden werden was neuer ist als C++03...



  • @LeMace sagte in Studentenverwaltung:

    Außerdem sollte alles vermieden werden was neuer ist als C++03...

    Das haben wir auch gelernt. Angeblich dauert es noch bestimmt einige Jahre, bis sich C++11 durchsetzt. Allerdings wurde mir hier dann gesagt, dass das nicht stimmen würde und dass ich den neuen Standard beherrschen sollte.



  • In der Klasse Student hast Du UB eingebaut. Du vermischst nach Belieben new[], malloc, delete[] und free. Entweder new[] und delete[] oder malloc und free.



  • @Wade1234 Firmen sind natürlich immer Programmierer am liebsten die die neuen Standards kennen, aber auch wissen wie sie mit C++03 zurecht kommen.
    Weil viele Firmen halt ein paar Projekte haben wo sie auf C++03 eingeschränkt sind, aber andere wo sie C++11, 14 oder 17 verwenden.



  • @john-0 sagte in Studentenverwaltung:

    In der Klasse Student hast Du UB eingebaut. Du vermischst nach Belieben new[], malloc, delete[] und free. Entweder new[] und delete[] oder malloc und free.

    Uups. War natürlich keine Absicht. Ich habe beim Schreiben mit malloc() und free() angefangen und dann auf new[] und delete[] umgestellt und da vergessen an den beiden stellen das free() zu ändern.

    @muemmel sagte in Studentenverwaltung:

    Am besten erst mal mit dem Prof abklären was er haben will, nicht dass er nachher schon daran scheitert den Quellcode lesen zu können.

    Nene, das geht schon aus dem Rest des Dokuments hervor daß C++ gewünscht ist.


  • Gesperrt

    @Wade1234 sagte in Studentenverwaltung:

    Das haben wir auch gelernt. Angeblich dauert es noch bestimmt einige Jahre, bis sich C++11 durchsetzt.

    C++11 kann vielleicht schon nächstes Jahr obsolet sein. Sich an eine Sprache wie C++ zu klammern, ist eben ein besonderes Abenteuer.



  • @RBS2 sagte in Studentenverwaltung:

    C++11 kann vielleicht schon nächstes Jahr obsolet sein. Sich an eine Sprache wie C++ zu klammern, ist eben ein besonderes Abenteuer.

    naja wenn du ein programm vorgesetzt bekommst, das 2005 entwickelt wurde, kannst du wohl nur schlecht alles auf c++17 umstellen und nur weil es in c++17 zig neue elemente gegenüber c++03 gibt, heißt das ja nicht, dass man das auch alles benutzen muss. von daher macht das halt schon irgendwie sinn, dass da an der hochschule nur c++03 gelehrt wird.



  • @Wade1234 sagte in Studentenverwaltung:

    naja wenn du ein programm vorgesetzt bekommst, das 2005 entwickelt wurde, kannst du wohl nur schlecht alles auf c++17 umstellen und nur weil es in c++17 zig neue elemente gegenüber c++03 gibt, heißt das ja nicht, dass man das auch alles benutzen muss. von daher macht das halt schon irgendwie sinn, dass da an der hochschule nur c++03 gelehrt wird.

    Nach der Logik könnte man auch gleich nur C unterrichten.



  • @hustbaer
    ja aber warum wird dann C++03 unterrichtet? angeblich ist das ja nicht aufgrund irgendwelcher stammtischüberlegungen der professoren so, sondern weil "die wirtschaft" das so haben will.



  • Kann ich mir nicht vorstellen. Das wird eher daher kommen, dass die Profs nichts anderes können.
    Ist aber auch nicht sooo relevant. Das C++, das im Studium gelernt wird, reicht eh nicht soweit, dass man irgendwelche Unterschiede zwischen C++03 und C++11 aufzeigen könnte. Es werden sogut wie immer nur Programmiergrundlagen beigebracht, meist auch noch technisch ziemlich schlecht. Aber sowas wie smart pointer oder stl hab ich in irgendwelchen Studiumsvorlesungen eh noch nie gesehen.


Anmelden zum Antworten