{"id":210,"date":"2019-01-22T10:02:27","date_gmt":"2019-01-22T10:02:27","guid":{"rendered":"http:\/\/devblog.cssec.de\/?p=210"},"modified":"2019-02-02T14:16:29","modified_gmt":"2019-02-02T14:16:29","slug":"forward-declarations-cpp","status":"publish","type":"post","link":"https:\/\/www.cssec.de\/blog\/2019\/01\/22\/forward-declarations-cpp\/","title":{"rendered":"Forward Declarations vs. Includes"},"content":{"rendered":"<p>Hallo da drau\u00dfen,<\/p>\n<p>heute mal wieder etwas C++-Sachen.<\/p>\n<p>In der Regel inkludiert man in C++-Klassen, speziell wenn es um Qt geht, verschiedene andere Klassen, seien es jetzt QString&#8217;s, QList-Elemente oder eben selbst definierte Klassen, die verschiedene Business-Logik kapseln. Diese packt man dann manchmal auch in member-Variablen oder nutzt diese in privaten oder public-Methoden als \u00dcbergabe-Parameter.<\/p>\n<p>Prinzipiell gibt es zwei Methoden, um Klassen einzubinden:<\/p>\n<ul>\n<li>\u00a0direkte includes in der h-Datei<\/li>\n<li>\u00a0Forward-Deklaration der Klasse<\/li>\n<\/ul>\n<p>F\u00fcr Neulinge erkl\u00e4re ich mal die beiden Dinge:<\/p>\n<h4>Include in der h-Datei<\/h4>\n<p>Hier mal ein Beispiel-Code f\u00fcr ein Klassen-Header, die einen Methode hat, der ein QString \u00fcbergeben wird. Die Datei speichere ich mal unter testklasse.h ab.<\/p>\n<pre lang=\"cpp-qt\">\r\n#ifndef TESTKLASSE_H\r\n#define TESTKLASSE_H\r\n#include <QObject>\r\n#include <QString>\r\n\r\nclass TestKlasse : public QObject {\r\npublic:\r\n  TestKlasse(); \/\/Konstruktor der Klasse\r\n  QString getHelloWorld() const;\r\n}\r\n#endif \/\/ TESTKLASSE_H\r\n<\/pre>\n<p>Normalerweise generiert Qt die meisten Sachen selbst. Trotzdem kurze Erkl\u00e4rung:<\/p>\n<p>Die erste Zeile wird dazu genutzt, um h-Dateien nicht mehrfach zu laden. Wird eine h-Datei inkludiert, wird gepr\u00fcft, ob das &#8222;Makro&#8220; TESTKLASSE_H schon einmal definiert (also geladen) wurde. Ist dies nicht der Fall, wird dieses &#8222;Makro&#8220; definiert (2.Zeile).<br \/>\nIn der dritten und vierten Zeile werden die entsprechenden referenzierten \/ genutzten Klassen, n\u00e4mlich QObject und QString geladen<\/p>\n<p>Dann wird die Klasse definiert als Unterklasse von QObject und Konstruktor und eine Methode getHelloWorld() definiert.<\/p>\n<p>Die letzte Zeile definiert dann das Ende des Makros (Laden der TestKlasse).<\/p>\n<p>Soweit, sogut. Durch die &#8222;#include&#8220;-Anweisungen haben wir entsprechend die genutzten Klassen direkt im H-File geladen. Die Klassendefinition steht nun sowohl im h-File als auch im zugeh\u00f6rigen cpp-File (also der Implementierung) zur Verf\u00fcgung.<\/p>\n<p>Nun noch die zugeh\u00f6rige Cpp-Datei (testklasse.cpp)<\/p>\n<pre lang=\"cpp-qt\">\r\n#include \"testklasse.h\"\r\n\/\/Definition des Konstruktors\r\nTestKlasse::TestKlasse() : public QObject() {\r\n\r\n}\r\n\r\nQString TestKlasse::getHelloWorld() {\r\n  return QString(\"Hello World!\");\r\n}\r\n<\/pre>\n<p>Wie man sieht, ben\u00f6tigt man hier keinen Include von QString und QObject mehr.<\/p>\n<h4>Forward-Deklaration der Klasse<\/h4>\n<p>Prinzipiell kann man die includes aber auch in die Implementierung verlagern. Dazu mal wieder unsere modifzierte h-Datei testklasse.h:<\/p>\n<pre lang=\"cpp-qt\">\r\n#ifndef TESTKLASSE_H\r\n#define TESTKLASSE_H\r\n\r\nclass QString;\r\nclass QObject;\r\n\r\nclass TestKlasse : public QObject {\r\npublic:\r\n  TestKlasse(); \/\/Konstruktor der Klasse\r\n  QString getHelloWorld() const;\r\n}\r\n#endif \/\/ TESTKLASSE_H\r\n<\/pre>\n<p>Hier f\u00e4llt auf, dass nun keinerlei Includes in der Datei vorhanden sind. Daf\u00fcr werden die Klassen QString und QObject in Zeile 4 und 5 Forward-Deklariert. Die Forward-Deklaration reserviert quasi den Namen der Klasse, die &#8222;Implementierung&#8220; erfolgt dann in der cpp-Klasse. Das hei\u00dft, in der cpp-Klasse m\u00fcssen dann die eigentlichen Includes gemacht werden.<\/p>\n<p>Somit sieht unsere testklasse.cpp folgenderma\u00dfen aus:<\/p>\n<pre lang=\"cpp-qt\">\r\n#include \"testklasse.h\"\r\n#include <QObject>\r\n#include <QString>\r\n\/\/Definition des Konstruktors\r\nTestKlasse::TestKlasse() : public QObject() {\r\n\r\n}\r\n\r\nQString TestKlasse::getHelloWorld() {\r\n  return QString(\"Hello World!\");\r\n}\r\n<\/pre>\n<p>Der Unterschied zum ersten Fall (include in h-Datei) ist, dass die includes in Zeile 2 und 3 nun im cpp-File zwingend definiert sein m\u00fcssen.<\/p>\n<p>Selbstverst\u00e4ndlich kann man die Dinge auch mischen, d.h. bestimmte Klassen \u00fcbers h-File laden und andere Klassen \u00fcber Forward-Deklaration laden.<\/p>\n<p>Da ich prim\u00e4r aus dem Java-Bereich komme und mir C++ sp\u00e4ter erst selbst angeeignet habe, war f\u00fcr mich die erste Methode immer die Wahl, auch wenn das nicht immer zu 100% funktioniert hat. Speziell, wenn man Kreuzabh\u00e4ngigkeiten zwischen Klassen besitzt, z.B. Klasse A beinhaltet eine Liste von Klasse B, die wiederum einen R\u00fcck-Verweis (als Pointer) auf die Klasse A besitzt. Wenn beide jeweils in der h-Datei auf das jeweilig andere verweisen, kommt ein gro\u00dfes Kuddelmuddel heraus und der C++-Compiler steigt aus.<br \/>\nIn solchen F\u00e4llen [b]MUSS[\/b] man eine Forward-Deklaration benutzen.<\/p>\n<h4>Wann nutzt man also nun welche Methode?<\/h4>\n<p>Vorteil der ersten Variante ist die klar definierte Struktur. Durch Anschauen der h-Datei bekommt man sofort einen \u00dcberblick, welche weiteren h\/cpp-Dateien entsprechend genutzt werden. Speziell zur Erzeugung von Klassenb\u00e4umen ist das nett.<br \/>\nGro\u00dfer Nachteil ist, dass bei irgendeiner \u00c4nderung der h-Datei automatisch auch alle abh\u00e4ngigen Klassen mit \u00fcbersetzt werden (inkl. h-Dateien). Das f\u00fchrt im schlimmsten Fall dazu, dass bei \u00c4nderung einer h-Datei das gesamte Projekt neu \u00fcbersetzt werden muss, wenn denn z.B. die h-Datei eine essentielle, \u00fcberall genutzte Datei ist. Bei gr\u00f6\u00dferen Projekten geht so ziemlich viel Compile-Zeit verloren.<\/p>\n<p>Das ist wiederum der Vorteil der zweiten Variante. Dort werden nur die Klassen, die direkt die ge\u00e4nderte Klasse (mit dem ge\u00e4nderten h-File) nutzen \u00fcbersetzt, die h-Datei bleibt entsprechend unanger\u00fchrt. Klassen, wiederum auf diese h-Datei verweisen m\u00fcssen dann nicht mehr \u00fcbersetzt werden.<br \/>\nHier spart man sich wirklich viel Compile-Zeit.<br \/>\nEin weiterer Vorteil ist, dass man z.B. unterschiedliche Implementierungen vornehmen kann, je nach Qt-Version, Makros.<\/p>\n<p>Ich pers\u00f6nlich nutze aktuell eine Misch-Variante. Da sich Qt-Klassen i.d.R. nicht \u00e4ndern, inkludiere ich diese \u00fcber die erste Methode, also direkt im h-File und nutze die Forward-Deklaration f\u00fcr meine eigenen Klassen.<br \/>\nDas spart dann Compile-Zeit und zumindest, was an Qt-Klassen inkludiert wird, kann ich direkt aus dem h-File ersehen.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hallo da drau\u00dfen, heute mal wieder etwas C++-Sachen. In der Regel inkludiert man in C++-Klassen, speziell wenn es um Qt geht, verschiedene andere Klassen, seien es jetzt QString&#8217;s, QList-Elemente oder eben selbst definierte Klassen, die verschiedene Business-Logik kapseln. Diese packt man dann manchmal auch in member-Variablen oder nutzt diese in privaten oder public-Methoden als \u00dcbergabe-Parameter. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":218,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7,6],"tags":[],"_links":{"self":[{"href":"https:\/\/www.cssec.de\/blog\/wp-json\/wp\/v2\/posts\/210"}],"collection":[{"href":"https:\/\/www.cssec.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cssec.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cssec.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cssec.de\/blog\/wp-json\/wp\/v2\/comments?post=210"}],"version-history":[{"count":10,"href":"https:\/\/www.cssec.de\/blog\/wp-json\/wp\/v2\/posts\/210\/revisions"}],"predecessor-version":[{"id":223,"href":"https:\/\/www.cssec.de\/blog\/wp-json\/wp\/v2\/posts\/210\/revisions\/223"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cssec.de\/blog\/wp-json\/wp\/v2\/media\/218"}],"wp:attachment":[{"href":"https:\/\/www.cssec.de\/blog\/wp-json\/wp\/v2\/media?parent=210"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cssec.de\/blog\/wp-json\/wp\/v2\/categories?post=210"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cssec.de\/blog\/wp-json\/wp\/v2\/tags?post=210"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}