Bug: std::async blockiert das Hauptprogramm

Weil in den Foren soviel Lob über die neue std::async Schnittstelle ausgeschüttet wurde, habe ich es einmal selber ausprobiert.

class Agent {
public:
  void hello() {
    for (auto i=0; i<100;i++) {
      std::cout <<"task1 "<<i<<"\n";
      sf::sleep(sf::milliseconds(1000/0.5));
    }
  }
  void run() {
    auto t1=std::async(std::launch::async,&Agent::hello,this);
  }
};

Kompiliert wird der Code erstmal, leider blockiert er anschließend die GUI. Eigentlich war es so gedacht, dass man im Background den Task startet und die GUI im Vordergrund weiterläuft. Nur irgendwie hält sich std::async nicht daran. Bei Stackoverflow ist das Problem bekannt, ich bin nicht der erste dem das auffällt. Witzigerweise hat die Lösung häufig damit zu tun, dass man dann doch auf std::thread zurückgreift und darin dann irgendwie den async Task kapselt. Tja, da kann man doch eigentlich gleich einen Thread erzeugen, den mittels detach in den Hintergrund schicken und fertig. Aber gut, dass ich das mal ausprobiert habe, so weiß ich wenigstens dass Tasks doch nicht so toll sind wie uns https://www.heise.de/ix/heft/Eine-Aufgabe-fuer-sich-3682705.html weißmachen möchte.

Advertisements

Globale Klassen in C++

Aktuelle versuche ich gerade ein Computerspiel zu programmieren. Dort gibt es das Problem, das einzelne Objekte Zugriff benötigen auf alle anderen Bereiche des Spiels und zusätzlich noch Multithreading verlangt wird. Diese Art des global-Zugriffs widerspricht ein wenig der objektorientierten Philosophie wonach eine Klasse nur immer die Variablen der eigenen Klasse sehen und verändern darf. Wie also kann man von dieser Regel ausnahmsweise abweichen? Mein erster Ansatz war die Verwendung von Pointern. Bekanntlich ist ein Pointer nur eine Adresse im Speicher und wer diesen verändert hat die Macht. Leider ergibt sich das Problem, dass Pointer die an Klassen via reference übergeben werden, zwingend im Constructor initialisiert werden müssen. Das geht jedoch nicht, weil die Klasse2 erst nach Klasse1 im Sourcecode vorhanden ist. Aber ich habe einen Ausweg gefunden der ein bizar anmutet, ohne Pointer auskommt und einen sehr mächtigen Ansatz bereitstellt zur Schaffung von globalen Klassen.

Die Idee lautet, zwei Ansätze miteinander zu kombinieren: das Erzeugen von globalen Variablen mit der Forward-Deklaration von Klassen. Hier ist das Minimalbeispiel:

 
#include <iostream>

class Physics {
public:
  int temp=2;
  void show();
};
class Game {
public:
  Physics myphysics;
  int frame=10;
  void run();
};
Game mygame;

void Physics::show() {
  mygame.frame++;
  std::cout <<mygame.frame<<"\n";
}
void Game::run() {
  std::cout <<"run game\n";
  mygame.myphysics.show();
}

int main() 
{
  mygame.run();
  return 0;
}

Es funktioniert so, dass es eine globale Game-Klasse gibt. Und jede Subklasse kann über diesen Pfad Zugriff erhalten. Damit diese Game-Klasse sauber instanziert werden kann, benötigt man Forward-Deklaration. Alternativ kann man auch Header-Files anlegen. Im Beispiel ist zu sehen, wie eine Subklasse von Game trotzdem Zugriff erhält auf die höhergelegene Ebene. Damit wird das Problem gelöst wie eine Child Klasse Zugriff erhält auf die Parent Klasse ohne komplizierte Pointer-Artithmetik bemühen zu müssen. Selbstverständlich ist der Codesnippet aus Sicht von sauberer Programmierung höchst problematisch. Weil jede Funktion im Grunde alles darf.

Vielleicht einige Details dazu. Im Programm wird eine globale Variable mygame definiert. In dieser Variable ist das komplette Spiel enthalten: mygame.physcis enthält die Physicengine, mygame.gui enthält die Variablen und Attribute der GUI und mygame.physics.car[0] enthält ein einzelnes Objekt des Spiels. Zugegeben, eine sehr abenteuerliche Vorstellung. Wenn man jedoch der Meinung ist, dass eine globale Klasse welche nur den SFML Screen enthält Sinn macht (schließlich wollen alle Klasse etwas darauf zeichnen), dann ist der obige Codesnippet nur die logische Erweiterung. Man hat dann nicht nur globalen Zugriff auf das Hauptfenster sondern darf auch die Physik-Engine ansprechen.

Was wäre die Alternative? Die Alternative wäre, globale Klassen zu verteufeln, also bereits eine globale GUI Klasse als Hexenwerk zu bezeichnen und stattdessen jede Klasse einzeln von GUI erben lassen um etwas ausgeben zu können. Und wenn man schon dabei ist, könnte man auch gleich die Verwendung von Pointern verbieten weil damit unkontrolliert auf den RAM zugegriffen wird. Nun, wie wir alle wissen sind jedoch globale Variablen und Pointer anerkannte Werkzeuge in der Programmiersprache C++ und der wichtigste Grund warum die Leute C++ und keine andere Sprache nutzen. Und auch im vorliegenden Fall eines Computerspiels zu Trafficsimulation ist die Verwendung gerechtfertigt, weil es ja darum geht die gewünschte Funktionalität zu realisieren.

ERFAHRUNGEN
Eine erster Test unter Realbedingungen verlief positiv. Der Zugriff auf die globale mygame Variable gelang tatsächlich aus unterschiedlichen Klassen. Ein wenig anders fühlt sich die Programmierung damit an, am ehesten kann man es mit der Löve Game Engine vergleichen. Das heißt, man hat nicht mehr den Eindruck in C++ sondern in Lua zu programmieren. Weil man eben nicht länger darüber nachgrübelt wie man Zugriff auf Variablen enthält sondern einfach nur den Pfad hinschreibt „mygame.physics.car.undsoweiter“ und dann eben weiß was da drinsteht. Wenn man das Programm startet läuft alles sauber durch, es gibt keine Speicherfehler oder ähnliches. Das einzige was man beachten muss ist zwingend Forward-Deklarationen (also Headerfiles) zu verwenden, sonst funktioniert die Initalisierung der Globalen Variable nicht korrekt. Schauen wir uns die Kernklasse etwas genauer an:

 
class Game {
public:
  Settings mysettings;
  GUI mygui;
  Physics myphysics;
};
Game mygame; /// global variable

Man hat ein Standard-Spiel mit den wichtigen Elementen und instanziert das in die bereits erwähnte mygame Variable. Mygame kann man sich wie eine Art von Pointer vorstellen, also ein Zeiger der auf den Anfang des Spiels verweist von wo aus man jeden Teilbereich auslesen kann. Egal in welcher Unterklasse gerade Sourcecode ausgeführt wird, er hat automatisch Zugriff auf diesen Pointer, gewissermaßen über Klassengrenzen hinweg. Theoretisch kann man über den absoluten Pfad sogar auf seine eigenen Varaiblenzugreifen. Das wichtigste aber ist, dass eine Sub-Klasse jetzt Zugriff erhält auf Klassen die darüber liegen (parent). Vergleichbar als wenn man in Unix „../“ eingibt, genauer gesagt ist mygame die Root-Wurzel „/“ von der aus man dann in die Programmbereiche absteigt „/myphysics.car“ beispielsweise.

Es gibt noch einen interessanten Nebeneffekt und zwar kann man auf Vererbung vollständig verzichten. Stattdessen ist Komposition das einzige Designpattern. Neue Klasse instanziert man in anderen Klassen und kann trotzdem später darauf zugreifen.

UPDATE
Leider hat sich das kleine Experiment nicht als zielführend erwiesen. Man hat zwar auf alles Zugriff, was aber dazu führt, dass bereits bei einem kleinen Miniprogramm mit 500 Zeilen Code in der Gliederungsansicht des Editors unendlich viele Variablen und Methoden angezeigt werden, die man prinzipiell alle auch aufrufen darf. Technisch geht es, der Compiler findet keinen Fehler, doch die Übersicht leidet darunter massiv. Es ist ungefähr so, als wenn man auf Klassen verzichtet und einfach die Funktionen alphabetisch untereinanderschreibt in einer großen Mega-Klasse. Ich will damit sagen, dass globale Variablen keine gute Idee sind, und man lieber nach Alternativen suchen sollte. Notfalls mit Parameterübergaben und ähnlichem arbeiten, aber der Scope einer Klasse sollte immer minimiert werden.

Umstieg auf C++ abgeschlossen

Zu Anfang war ich noch skeptisch ob man in C++ genauso effizient Software schreiben kann wie in Python. Gerade für Anfänger ist wohl Python die leichtere Programmiersprache. Doch nachdem ich mich etwas mit C++ beschäftigt habe muss ich sagen, dass die Sprache besser ist als ihr Ruf. Die meisten Stolpersteine kann man durch etwas Lektüre ausräumen und mit etwas Übung fühlt sich C++ irgendwann genauso an wie Python. Das heißt, man bemerkt die Sprache gar nicht mehr, und wenn dochmal ein Compilerfehler auftaucht, korrigiert man einfach das fehlende Semikolon oder verändert den Datentyp.

Von der Programmiergeschwindigkeit her selber würde ich sagen, dass man in C++ ungefähr gleichschnell unterwegs ist wie in Python, vielleicht mit einem minimalen Vorteil für Python, weil man dort weniger Optionen zur Auswahl hat und folglich weniger falsch machen kann. Der große Vorteil von Software die in C++ erstellt wurde liegt jedoch darin, dass man damit Anschluss an eine Community erhält die um einiges größer ist als die Python Community. Man muss nicht groß rätselraten wie man auf vorhandene Libraries zugreift oder ob man möglicherweise auf die CORBA oder REST SChnittstelle zurückgreift sondern spricht die vorhandene C++ Library nativ an. Desweiteren ist der Code den man selber schreibt, standardmäßig library-ready. Damit ist gemeint, dass man ihn nicht erst nach C übertragen muss, wenn man die maximale Performance benötigt sondern der Code so wie er ist weiterverwendet werden kann. Das dürfte gerade bei größeren Projekten von Vorteil sein.

Es ist keineswegs vermessen von C++ Programmierern zu behaupten, dass sich alle anderen Programmierprogrammierer bitteschön nach ihnen zu richten haben. Eine Sprache die es mit C++ aufnehmen könnte gibt es aktuell nicht. Wenn also jemand sagt, dass C++ und sonst gar nichts in einem Softwareprojekt verwendet wird, kann man diese Haltung nicht wirklich kritisieren. Komischerweise wird es nur selten so explizit vorgetragen. Im Mainstream hat sich stattdessen eine Anything goes Haltung durchgesetzt, wonach Java, Python, C#, D, go, C, PHP und Dylan allesamt gleichberechtigte Sprachen seien und deren Einsatz sehr vom jeweiligen Thema und von den konkreten Personen abhängt. Das ist so jedoch nicht richtig. Richtig ist hingegen, dass es bisher der C++ Community ideologisch nicht gelungen ist, ihre Sprache als die einzig richtige Sprache zu positionieren. Aktuell gibt es nur wenige und schlecht geschriebene Einsteigerbücher für C++. Auch fehlt es an Anleitungen wie man mit C++ Webanwendungen baut. In diese Nische sind die oben genannten Alternativ-Sprachen vorgedrungen, nicht weil sie mächtiger wären, sondern weil sich Fürsprecher gefunden haben. Technologisch sind Java und Co jedoch eine Sackgasse. Und ich wage mal die These, dass Java nicht wirklich leichter zu verstehen ist als C++.

Die noch offene Frage lautet, wie genau man mit C++ größere Programme schreibt. Ähnlich wie in Python gibt es zwar das Modulkonzept, aber der Flaschenhals ist wie so oft der Mensch. Der Königsweg um größere C++ Projekte durchzuführen ist das Versionsverwaltungssystem git. Erst damit lassen sich Softwareteams bilden. Der einzelne C++ Programmierer bleibt im Zweifel genauso klein wie der einfache Python Coder der ein 500 Zeilen Script zusammenbaut.

Dennoch habe ich an C++ zwei Kritikpunkte ausfindig machen können. Zum einen gibt es bis heute keinen richtigen Interpreter. Ich habe zwar root-cling gefunden, bekomme das Programm aber nicht gestartet, und der C++ Interpreter auf Repl.It funktioniert nur über GCC. Der Zweite Kritikpunkt ist das schlechte Angebot an Literatur. Auf Deutsch gibt es überhaupt kein gutes Tutorial und auf Englisch behandeln die meisten nur die Sprache als solche. Woran es Bedarf gibt ist ein deutschsprachiges Tutorial was an SFML orientiert ist und Gebrauch macht vom C++17 Standard. Wenn man sich näher damit beschäftigt wird man feststellen, dass C++ um einiges mächtiger ist als Python, insbesondere wenn man Multithreading durchführt. Also typische Weltraumabenteuer programmiert, bei denen hunderte Sprites gleichzeitig durch die Map leveln.

Globale Objekte in C++

Gerade Einsteiger schrecken davor zurück in C++ globale Objekte zu definieren. Sie glauben, es würde grundlegende Prinzipien der objektorientierung Programmierung verletzen. Stattdessen versuchen sie mit Singleton-Pattern, Abstrakten Basisklassen und Get/Set Kombinationen zu arbeiten. Dabei sind globale Objekte und C++ sehr kompatibel zueinander. Ja, die Verwendung von globalen Variablen ist sogar das grundlegende Design-Prinzip. Dazu ein kleiner Exkurs wie überhaupt die ersten OOP Sprachen entstanden sind.

In einem normalen C Programm schreibt man 5 Methoden hin und um sich Schreibarbeit zu sparen definiert man oben im Header eine globale Variable auf die alle Funktionen zugreifen können. Es gibt jetzt zwei Parteien: die einen sagen, das wäre ein Nogo, weil dadurch der Code nicht mehr wartbar sei, die andere Fraktion sagt: hey super, lass uns um das ganze noch eine Klasse bilden und wir haben OOP erfunden. Im Detail bedeutet objektorientierte Programmierung dass mehrere Funktionen auf die selbe Variable zugreifen können. Das heißt, Funktion1 verändert die Variable und Funktion2 arbeitet mit dem geänderten Wert dann weiter. Viele Programmierer aus dem C++ und Java Umfeld haben dieses Designprinzip verdrängt, aber es ist genau das was sie jeden Tag tun, wenn sie Klassen anlegen. Innerhalb einer Klasse ist die Variable global.

Kommen wir jetzt zur Einstiegsfrage zurück ob man globale Objekte in C++ anlegen sollte. Der Grund warum man das machen möchte ist simpel: es geht darum mit Funktionen aus unterschiedlichen Objekten auf ein und dieselben Daten zuzugreifen. Und natürlich darf man das, um diese Hierachie macht man anschließend noch eine weitere Klasse und kapselt das ganze.

Natürlich kann man globale Variablen umgehen, und zwar auf ähnliche Weise wie man sie auch in C vermeiden kann. Man übergibt einfach den Funktionen die Variable als Parameter. Auf C++ übertragen bedeutet es, dass man die Objekte mit einem Pointer auf die Variable initialisiert und dadurch dann Zugriff erhält. Programmtechnisch steht dann in jedem Objekt drin, dass es Zugriff hat auf die Variable. Aber ist dieser Stil wirklich einfacher zu verstehen als eine explizite globale Variable? Wird dadurch die Programmierung beschleunigt oder reduzieren sich Bugs? Richtig ist, dass globale Variablen wie auch objektorientierung erfunden wurden aus Bequemlichkeitsgründen und Bequem sind sie tatsächlich. Grundsätzlich ist es zwar richtig, dass man mit C++ die Dinge kapseln lernt aber wie bitteschön will man eine Bildschirmausgabe kapseln? Im Regelfall gibt es genau einen Bildschirm auf den alle Objekte draufschreiben wollen. Erge benötigen sie Lese/Schreibrechte sonst geht es nicht. Ob die Objekte nun einen Pointer auf den Bildschirm oder eine globale Variable erhalten macht keinen Unterschied. Die Frage ist lediglich ob man das globale Objekt noch um weitere Variablen und Methoden ergänzen kann um damit die Wiederverwendbarkeit von Code zu fördern.

In meinem eigenen Projekt habe ich selbstverständlich ein globales Objekt in Gebrauch. Es ist der Kern des Programms und enthält grundlegende Routinen sowie extrem wichtige Datenstrukturen. Um diesen Kern herum gruppieren sich die anderen Objekte. Das eines dieser Objekte nicht auf den Kern zugreifen möchte ist ausgeschlossen. Weil das bedeuten würde, dass die Routinen und Datenstrukturen im Core doch nicht so wichtig waren.

Threads in SFML und C++

Die SFML Bibliothek bringt eine eigene Thread-Verwaltung mit „sf::Thread“. Nach etwas herumprobieren mit dem This-Schlüsselwort beim Starten des Threads habe ich diese tatsächlich auch innerhalb einer Klasse aufrufen können. Nur irgendwie blockiert der Thread anschließend die Mainloop. Auch andere Forenuser welche damit herumexperimentieren machen eher einen verzweifelten Eindruck. Aber kein Problem, der Plan B lautet, einfach std::thread zu nutzen, was zu folgendem Code führt:

std::thread t1;
t1=std::thread(&GUI::task1, this);
t1.detach();

dann noch schnell den „-pthread“ als Compilerparameter anfügen (übrigens gilt das auch für clang) und schon kann man sich an einem perfekten Multitasking erfreuen bei dem einerseits die Game-Engine schön mit 60fps arbeitet, gleichzeitig im Background als task1 aber auch noch der Roboter automatisch seine Bahnen zieht. Zugegeben, auch Python beinhaltet eine Thread Funktion, allerdings ist diese nicht ganz so leistungsfähig wie die von C++. Bei einfachen Spielen fällt das noch nicht so auf, wenn man aber große Schlachten mit hunderten Krieger bewältigen will ist std::thread einfach leichter in der Handhabung.

Mal überlegen, was gibt es noch über Threads zu sagen? In manchen Foren wird noch diskutiert, ob man anstatt Threads nicht lieber etwas anderes nutzen sollen um eine ingame -AI zu steuern. Meist läuft das daraufhinaus in der Game-Loop noch eine Unterroutine anzulegen die dann einen Plan abarbeitet wo drinsteht was der Roboter als nächstes macht. Also einen selbstprogrammierten Interpreter zu nutzen. In Python habe ich damit herumexperimentiert und bin irgendwann entnervt auf die eingebauten Threads gewechselt, weil man darin die Standard-Sprachbefehle wie if und for verwenden kann. Bei eineme eigenen Interpreter muss man das erst manuell hinprogrammieren und es ist zweifelhaft ob das auf Anhieb klappt. Auch in C++ ist nach meiner Erfahrung das Thread Pattern optimal geeignet wenn man eine KI im Background laufen lassen will, das betrifft einen einzelnen NPC aber auch 10 Stück auf einmal, wo dann jeder einen Thread erhält.

Der Begriff Thread bedeutet dabei, dass der Prozess Zugriff erhält auf den Speicher als gemeinsam mit dem Rest des Programms Werte verändert. Und ja, mögliche Programmabstürze sollte man einkalkulieren. Doch im Regelfall erhält man eine überragende Performance und leicht zu debuggen ist das ganze auch. Für Linux selber sieht es ohnehin aus, als wäre es nur ein Programm, und im Worstcase kann man das mit pkill ganz leicht terminieren

Richtig Spaß machen Threads wenn man die GUI in 60fps laufen lässt und die Pausen des Bots ebenfalls auf ein Minimum reduziert. Man erhält dann einen Roboter der extrem zügig durch das Level eilt, ohne dass es die kleinsten Ruckler gibt. Trotzdem bleibt die CPU Auslastung schön niedrig und man bekommt ein Gefühl dafür was die Hardware prinzipiell zu leisten im Stande ist. Leider steckt der Flaschenhals viel zu oft in der Software, weil die KI des Bots nicht fehlerfrei programmiert wurde.

Modernes C++ als Alternative zu Python

C++ besitzt noch immer das Image einer schwer zu erlernenden Programmiersprache. Aber in den letzten 5 Jahren hat sich da einiges zum besseren gewandelt. Aktueller Soucecode in C++ lässt sich fast so simpel schreiben wie Python Code. Beispielsweise kann man als Rückgabewert für eine Methode einen Array-Vektor angeben. Anders als in C ist es also möglich, mehrere Werte zurückzugeben. Man speichert die Werte einfach in das std::array hinein und übergibt das am Ende der Funktion mit „return result“, also ungefähr so wie man es auch in Python machen würde. Weiterhin ist es möglich in vielen Fällen auf eine Variablendeklaration zu verzichten, stattdessen nutzt man das Auto Keyword und lässt den Compiler entscheiden ob es ein Integer oder ein String ist. Wenn man dann noch berücksichtigt, dass man über repl.it sogar eine interpretierte Ausführung erhält so ist modernes C++ schon ziemlich dicht dran am Komfort den Python bietet. Einiges vermisst man aber trotzdem. Das iPython Notebook oder die matplotlib gibt es in C++ nicht. Was man jedoch erhält ist eine Sprache die sich ausgezeichnet eignet um Bibliotheken zu schreiben, was aus Effizienzgründen in größeren Projekten wichtig ist. Auch erhält man mit Doxygen einen ausgezeichneten Dokumentengenerator, so dass ich alles in Allem sagen würde dass C++ besser ist als Python.

UPDATE
Matplotlib gibt es inzwischen auch für C++: https://github.com/lava/matplotlib-cpp Der Sourcecode bis man ein fertiges Diagramm erzeugt ist fast identisch mit dem was Python User verwenden. Wieder ein Grund mehr auf C++ umzusteigen. Genau genommen ist C++ eine unterschätzte Sprache, deren einzige Schwäche darin besteht, dass es nur wenig gute Tutorials gibt. Python, Java und C# sind hingegen ein Hype. Technologisch handelt es sich um Sackgassen, dafür gibt es auszeichnete Bücher die Einsteigern erklären wie man damit schnelle Erfolge erzielt. Im Grunde müsste mal jemand zu C++ ein Buch schreiben wo erstens nur die neusten Features vorgestellt werden, und zweitens die obige Matplotlib und SFML verwendet wird um in aufregend wenige Sourcecode komplette GUI Applikationen hochzuziehen. Das man also C++ explizit als Konkurrenz zu Python positioniert und sich an die Zielgruppe Anfänger wendet.

Vererbungshierarchien in C++

Zugegeben, in Python ist vieles leichter. Es ist dort relativ schwer einen Fehler zu erzeugen. Man kann aber durchaus auch in C++ eine Vererbungshierachie aufbauen. Die dabei auftretenden Hindernisse werden im folgenden erläutert. Fangen wir mit einem simplen Beispiel an: wir haben zwei Klassen und die eine Klasse soll auf die andere zugreifen. Der einfachste Weg wäre natürlich alles in eine Klasse zu schreiben. Aber, das führt häufig zu überlangen Klassen und die best-practice Methode im Refactoring besteht darin, die Klassenlänge stur auf 100 Lines of Code zu begrenzen. Daher brauchen wir unbedingt zwei Klassen, eine Hauptklasse für die GUI und eine GUIaddon Klasse welche weitere Funktionen bereitstellt:

#include <iostream>

class GUIplugin {
public:
  void show() {
    std::cout << "show\n"; 
    //std::cout <<temp<< "\n"; 
  }
};

class GUI : public GUIplugin {
public:
  int temp = 0;
  GUI() {
    temp = 10;
    show();
  }
};

int main() 
{
  GUI mygui;
  return 0;
}

Soweit der Code. Die Klassen wurden vorbildlich mittels Vererbung zusammengeschaltet und der Aufruf der „show()“ Methode ruft Code aus der Super-Class auf. Wer sich den Kommentar näher angeschaut hat, ahnt vermutlich was jetzt kommt. Weil eigentlich soll ja nicht nur Hello World ausgegeben werden, sondern eigentlich wollen wir auf die Variablen der Subclass zugreifen, also einen Downcast machen. Doch oh weh, wenn man man die Zeile auskommentiert und die Variable „temp“ ausgeben will gibt es einen bösen Compilerfehler. Genauer gesagt lautet er: „error: use of undeclared identifier ‚temp'“. Was kann man da machen? Die naheliegenste Antwort wäre auf Vererbung zu verzichten und stattdessen Komposition zu verwenden. Man trennt also die Klassen physisch und übergibt im Konstruktur einen Pointer auf die temp Variable. Aber, erstens sind Pointer nicht unbedingt das, was Einsteiger an C++ zu schätzen wissen und zweitens gehören die Klassen inhaltlich ja zusammen und ein Compositor Pattern würde keinen Sinn machen. Was man stattdessen tun sollte ist, eine Art von Abstrakter Klasse zu verwenden. Nicht eine richtige laut Lehrbuch, sondern nur die Idee als solche. Eine abstrakte Klasse bedeutet, dass dort zwar Attribute gespeichert sind, diese aber von rangniederen Klassen verwendet werden. Genau sowas brauchen wir hier, der abgeänderte Sourcecode sieht so aus, dass man die Variable hochzieht in die Superklasse:

#include <iostream>

class GUIplugin {
public:
  int temp = 0;
  void show() {
    std::cout << "show\n"; 
    std::cout <<temp<< "\n"; 
  }
};

class GUI : public GUIplugin {
public:
  GUI() {
    temp = 10;
    show();
  }
};

int main() 
{
  GUI mygui;
  return 0;
}

Wie schon gesagt, ist das keine echte abstrakte Klasse, aber das Kompilieren funktioniert einwandfrei. Und jetzt haben wir das was wir wollten: zwei sich ergänzende Klassen, und beide können auf alle Variablen zugreifen. Wer es perfekt machen will und eine echte abstrake Klasse erstellen möchte muss eine Pseudofunktion mit virtual deklarieren und diese in der Subklasse dann nochmal daklarieren. Darüber wird verhindert, dass man die GUIplugin Klasse einzeln instanzieren kann. Sie ist absofort nur noch ein Bauplan von der man andere Klassen ableiten kann:

#include <iostream>

class GUIplugin {
public:
  int temp = 0;
  virtual void f() = 0; // force abstract class
  void show() {
    std::cout << "show\n"; 
    std::cout <<temp<< "\n"; 
  }
};

class GUI : public GUIplugin {
public:
  GUI() {
    temp = 10;
    show();
  }
  void f() {}; 
};

int main() 
{
  GUI mygui;
  mygui.temp = 11;
  mygui.show();
  return 0;
}

Jetzt mag man sich als Einsteiger verdutzt fragen wozu das jemand einsetzen möchte. Die Antwort hat erneut etwas mit „Lines of Code“ zu tun, bei einem C++ Programm was maximal 1000 Zeilen lang ist, braucht man mit Sicherheit keine abstrakten Klassen, ja ein solches Programm kann man sogar komplett ohne Klassen programmieren und einfach 4-5 Methoden untereinander schreiben. Interessant wird es hingegen wenn größere Projekte anstehen mit 10k oder nochmehr Lines of Code. In solchen Fällen haben die Programmierer ein großes Bedürfnis nach Klassen die maximal 100 Lines of Code lang sind, das Zauberwort hier lautet Refactoring. Also das Aufsplitten von langen Klassen in kurze Klassen. Wenn man ein 10k Programm derartig aufteilt ergibt sich rechnerisch 10000/100=100 Klassen. Und die hängen irgendwie alle zusammen in einem riesigen Vererbungsnetz. Dort sind mehrere Klassen hinterinander geschaltet und bei solchen Ketten benötigt man abstrakte Klassen. Ganz oben an der Spitze sowieso, aber m anchmal auch mittendrin um Informationen zu bündeln.

Warum Programmierer gerne kurze Klassen haben anstatt Lange hat damit zu tun, dass üblicherweise Sourcecode einer permanenten Veränderung unterliegt. Damit ist gemeint, dass wenn das Projekt richtig betrieben wird, praktisch jede Codezeile im Laufe der Zeit ausgetauscht wird gegen besseren Code. Um diesen permanente Überarbeitung zu realisieren darf man sich immer nur eine Klasse auf einmal vornehmen. Man ändert nur einen abgegrenzten Bereich, und wenn der Bereich überschaubar ist, geht das umso leichter.