Qt für C++ Anfänger: Aufteilung des Quellcodes

Aus Wikibooks

Der Quellcode soll jetzt auf mehrere Dateien verteilt werden, um eine bessere Übersicht zu behalten. Dazu sollen die Funktionen zum Addieren und zum Subtrahieren in einer jeweils eigenen Datei liegen. Ferner wird es eine Datei geben, die für übergreifende Funktionen (z.B. das UI) zuständig ist. Jede Funktion wird in eine eigene Klasse ausgegliedert, jede Klasse besteht aus zwei Dateien:

<Klassenname>.cpp
<Klassenname>.h

Insgesamt werden wir die folgenden Dateien haben:

myAdd.cpp
myAdd.h

mySub.cpp
mySub.h

Taschenrechner.cpp
Taschenrechner.h

main.cpp

Taschenrechner.ui

Eine Anmerkung sei gemacht, die neuen Klassen wurden myAdd und mySub genannt. Auch hier muss wieder mit der Namensgebung aufgepasst werden, da bestimmte Namen schon belegt sind. Wir hätten sie nicht add und sub, sehr wohl aber Add und Sub nennen können. Die Dateien myAdd.cpp/.h und mySub.cpp/.h sehen natürlich sehr ähnlich aus und wir werden nur die Klasse myAdd im Detail besprechen, für mySub wird der Quellcode ohne weiteren Kommentar zur Verfügung gestellt.

myAdd.h[Bearbeiten]

Hierbei handelt es sich wieder um eine Headerdatei, die im Prinzip nichts Ungewöhnliches enthält.

//--- myAdd.h - start ---
#ifndef MYADD_H
#define MYADD_H

#include <QWidget>

class Taschenrechner;

class myAdd : public QWidget{
	Q_OBJECT

Taschenrechner *myTaschenrechner;

public:
	myAdd (Taschenrechner*);
	~myAdd();
private slots:

};

#endif //MYADD_H
//--- myAdd.h - end ---

Die Klasse myAdd ist abgeleitet von der Klasse QWidget. Dies ist notwendig, damit die Klasse myAdd alle grundsätzlichen Qt Eigenschaften besitzt. Es gibt eine Klassenvariable myTaschenrechner von Typ Taschenrechner, die unsere Hauptklasse ist. Damit diese Variable erzeugt werden kann, muss der Typ Taschenrechner deklariert werden, sprich der Compiler muss wissen, dass es eine Klasse Taschenrechner gibt. Dies passiert mittels der forward declaration

class Taschenrechner;

Diese Variable muss beim Anlegen einer Instanz der Klasse myAdd belegt werden. Erzwungen wird das, da im Konstruktor von myAdd diese Variable übergeben werden muss. Notwendig ist dies, da wir aus der Klasse myAdd heraus auf Objekte (=UI Elemente) der Klasse Taschenrechner zugreifen wollen. Diese UI Elemente liegen gemäß unseres Designs (die Qt Designer ui-Datei wird aus Taschenrechner.cpp heraus dargestellt) in dieser Klasse und könnten ohne Zugriff auf diese Klasse nicht gefunden/angesprochen werden.

myAdd.cpp[Bearbeiten]

Im Grundgerüst der Klasse myAdd werden erstmals nur der Konstruktor und der Destruktor implementiert. Für den Konstruktor muss überlegt werden, wie der Pointer auf die Klasse Taschenrechner übergeben werden kann. Eine Möglichkeit ist es, beim Aufruf des Konstruktors den Wert an einen Pointer TRechner zu übergeben und diesen dann an myTaschenrechner zu übergeben.

//--- myAdd.cpp - start ---
#include "myAdd.h"
#include "Taschenrechner.h"

myAdd::myAdd(Taschenrechner *TRechner){
	myTaschenrechner = TRechner; 
}

myAdd::~myAdd(){
}
//--- myAdd.cpp - end ---

Es sei angemerkt, dass etwas in der Art von

myAdd::myAdd(Taschenrechner *myTaschenrechner){
}

nicht funktioniert, man muss also wirklich eine Variable übergeben und diese dann weiter reichen. Das ist sicherlich nicht sehr elegant, daher gibt es eine andere Möglichkeit Klassenvariablen zu initialisieren. Dabei handelt es sich um die so genannten Initialisierungslisten.

//--- myAdd.cpp - start ---
#include "myAdd.h"
#include "Taschenrechner.h"

myAdd::myAdd(Taschenrechner *TRechner):myTaschenrechner(TRechner){
}

myAdd::~myAdd(){
}
//--- myAdd.cpp - end ---

Man erkennt im Code wohl, wie diese Liste funktioniert, die Variable wird mit einem ":" angehangen und der Wert zur Initialisierung wird in runden Klammern übergeben. Auf diese Art und Weise können auch mehrere Variablen initialisiert werden.

Erzeugen von Instanzen der neuen Klassen[Bearbeiten]

Die neuen Klassen wurden jetzt definiert. Sie haben bisher noch keine Funktionen implementiert. Das wird später kommen. Die Frage ist jetzt noch, wie man diese Klassen wirklich nutzt. Nur vom Erstellen der Header und cpp Files werden die Klassen noch nicht verwendet. Man muss im bisherigen Coding eine Instanz der neuen Klassen anlegen, dies geschieht über den "new" Operator innerhalb des Konstruktors der Klasse Taschenrechner.

myAdd *addWidget = new myAdd(this);
mySub *subWidget = new mySub(this);

Hier wird ein Pointer addWidget/subWidget vom Typ myAdd/mySub angelegt (myAdd *addWidget). Der Pointer wird durch die Funktion myAdd() initialisiert. Dazu muss bei Klassen der Operator "new" verwendet werden im Gegensatz zu intrinsischen Datentypen, z.B. int a = 1, bei denen kein "new" verwendet wird. Die Funktion myAdd() ist der Konstruktor der Klasse myAdd. So, wie diese Klasse definiert ist, muss immer ein Parameter übergeben werden. Dieser Parameter ist der Pointer auf die Instanz der Klasse Taschenrechner. Diesen Pointer kann man im Konstruktor mit der Anweisung "this" übergeben.
Wichtig: Damit diese Funktionen in Taschenrechner.cpp bekannt sind, darf man nicht vergessen, die Header-Dateien der Klassen mittels #include einzubinden.

Bevor jetzt der Quellcode mittels make übersetzt werden kann, muss berücksichtigt werden, dass neue Dateien erzeugt wurden. Diese sind im Makefile nicht angegeben, so dass es beim Übersetzen zu einer Fehlermeldung kommen wird. Einfach mal ausprobieren. Damit das Makefile angepasst wird, nochmals

qmake -project
qmake

ausführen. Jetzt sollte das Programm mittels make übersetzt werden können. Es ist allerdings noch keine neue Funktionalität enthalten. Weiterhin wird eine Warnung ausgegeben, dass addWidget und subWidget nicht benutzt werden. Diese Warnung kann ignoriert werden, da wirklich mit den Objekten noch nichts passiert.