JLI Spieleprogrammierung Foren-Übersicht JLI Spieleprogrammierung

 
 FAQFAQ   SuchenSuchen   MitgliederlisteMitgliederliste   BenutzergruppenBenutzergruppen 
 medals.php?sid=183c82a5806dea21be533cabf91f2172Medaillen   RegistrierenRegistrieren   ProfilProfil   Einloggen, um private Nachrichten zu lesenEinloggen, um private Nachrichten zu lesen   LoginLogin 

Dateien aufsplitten

 
Neues Thema eröffnen   Neue Antwort erstellen    JLI Spieleprogrammierung Foren-Übersicht -> Entwicklung
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen  
Autor Nachricht
Legolas87
JLI'ler


Alter: 37
Anmeldedatum: 22.10.2003
Beiträge: 131

Medaillen: Keine

BeitragVerfasst am: 11.03.2008, 13:08    Titel: Dateien aufsplitten Antworten mit Zitat

gibts irgendwo ne anleitung wie man dateien richtig aufteilt und wo man welche header einbindet und ob nun eine variable inne header oder in eine cpp datei kommt usw.

ich glaube nämlich dass es nicht elegant ist in jede datei jede jede header einzufügen. gibts vielleicht tricks mit denen man testen kann ob die header datei vielleicht nicht benötigt wird oder schon auf anderem wege eingebunden wurde. vielleicht gibts ja auch ein tutorial.

gruß
Legolas
_________________
I can see I'm going blind.
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden Website dieses Benutzers besuchen
GreveN
JLI Master


Alter: 38
Anmeldedatum: 08.01.2004
Beiträge: 901
Wohnort: Sachsen - Dresden
Medaillen: Keine

BeitragVerfasst am: 11.03.2008, 13:50    Titel: Antworten mit Zitat

In Header kommen prinzipiell nur Deklarationen, in "echte" Source-Dateien Definitionen. Sprich in die Header die Funktionsprototypen, Klassenrümpfe, Vorwärtsdeklarationen, Variablendeklarationen (die ja im Prinzip auch nur Prototypen sind) etc. pp. und die Implementierungen, Initialisierungen etc. in die Source-Dateien.

Im Prinzip ist das das ganze Geheimnis, wenn du nun irgendeine Funktion, Klasse oder was auch immer brauchst, bindest du nur die jeweilige Header ein, dadurch machst du die Funktion/Klasse/... "bekannt". Stelle dem Aufrufer also so viel Information zur Verfügung wie er unbedingt braucht, aber nicht mehr! Es interessiert ja an diesem Punkt noch nicht, was innerhalb der Funktion/Klasse/... konkret gemacht wird, sondern lediglich, dass es eben eine solche gibt, die genau die geforderte Signatur hat. Deshalb reicht es auch immer Header einzubinden. Wenn du nun deinen Code geschrieben und kompiliert hast, kommt der Punkt an dem diese "rufe die Funktion foo auf"-Verweise aufgelöst werden müssen. Anders gesagt: Hinter diese ganzen Verweise müssen konkrete Implementierungen. Das geschieht dadurch, dass der Linker die zuvor kompilierten Sources gegenlinkt. Du kannst dir das so vorstellen, dass du jedes mal, wenn du an den Punkt kommst, wo du eine Funktion oder Methode aufrufst an die Stelle einen kleinen Link einfügst und eine Notiz machst, die dir später sagt, worauf der Link später gesetzt werden soll. Beim Linken werden dann diese Links auf die konkreten Implementierungen gesetzt.

Etwas schwammig wird das bei der Nutzung von Templates, inline etc., aber ich denke, das ist im Moment nicht dein Problem.

Ein sehr geläufiges Problem entsteht dadurch, dass man 2 Deklarationen hat, von denen jede, die jeweils andere braucht. Also in den Headern sowas in der Art wie: A bindet B ein und B bindet A ein auftaucht. Wenn du verstanden hast was ich oben versucht habe zu erklären, löst sich das Problem auch fast von allein. A bindet B ein, das bedeutet irgendwo innerhalb von A gibt es eine Funktion/Klasse/... die einen Signatur/Prototyp/Deklaration/... benötigt, die in B steht, das bedeutet es reicht, diese Deklaration in A einfach manuell bekannt zu machen. Das heißt, du sagst in A es existiert z.B. ein "foo". Das in B auch steht es existiert ein "foo" interessiert dabei nicht. Ist auch völlig uninteressant, weil du nichts kontroverses darüber aussagst, was "foo" macht, sondern nur, dass eben dieses existiert, damit kollidieren A und B nicht. Genauso kannst du sagen, es existiert eine Klasse A via "class A;". Beim Linken werden dann wieder gegen diese "Links" Implementierungen gesetzt.

Manchmal sagt etwas Code mehr als tausend Worte:
CPP:
#ifndef _A_HPP_
#define _A_HPP_

void foo(void);
class bar;
int x;

/* Alle diese Anweisungen haben gemein, dass sie nichts über irgendeine Implementierung aussagen, sondern lediglich es gibt eine Funktion foo, eine Klasse bar und eine Variable x. */

#endif


Nun das erläuterte "Problem":
CPP:
#ifndef _A_HPP_
#define _A_HPP_

#include "b.hpp"

void foo(A a);

class B
{
...
};

#endif

Es wird also gesagt, es existiert eine Funktion foo die ein Objekt vom Typ A entgegennimmt (dessen Existenz allem Anschein nach in der Datei b.hpp behauptet wird) und es existiert ein Typ B mit folgendem Aufbau...

CPP:
#ifndef _B_HPP_
#define _B_HPP_

#include "a.hpp"

void bar(B b);

class A
{
...
};

#endif

Hier verhält es sich analog, nun ergibt sich für Anfänger häufig das Problem, dass sie nicht wissen, wie sie diesen Zyklus durchbrechen sollen.

Wenn wir uns die Forderungen nochmal genau anschauen, so fällt auf, dass sowohl foo als auch bar lediglich fordern, dass sie ein Argument vom Typ A bzw. Typ B bekommen sollen. Sie stellen keine Ansprüche daran wie dieser Typ aufgebaut sein muss und dürfen das an dieser Stelle auch gar nicht. Nur, wenn wir wissen, dass foo und bar jeweils nur fordern, dass der jeweilige Argument-Typ existieren muss, reicht es auch im Prinzip diese Forderung zu erfüllen.

Exemplarisch:
CPP:
#ifndef _A_HPP_
#define _A_HPP_

/* Achtung: An dieser Stelle muss b.hpp NICHT eingebunden werden! */


/* Wir sagen: Es existiert ein Typ A, mehr nicht! Das kollidiert nicht mit den Aussagen die b.hpp über den Typ A getroffen werden! */
class A;

void foo(A a);

class B
{
...
};

#endif


Etwas ausführlicher als geplant, aber ich hoffe das trägt zum allgemeinen Verständnis bei. Ich mache sehr häufig die Erfahrung, dass Anfängern diese "Grundstruktur" der Sprachen C/C++/... nicht klar ist, weil man von diesen typischen Basic-Anfängersprachen her gewohnt ist irgendwo einen Einsprungpunkt zu haben, von dem aus man dann systematisch, linear den ganzen Code abklappert und erreichen muss. Aber eben diese Trennung von Deklartionen und Definitionen, die sich auf jedes Sprachkonstrukt anwenden lässt, bringt eben viele Vorteile mit sich. Wer das als Anfänger verstanden hat, versteht auch, warum man z.B. eben keine *.cpp-Dateien einbinden muss - ganz einfach weil der Compiler Implementierungen übersetzt und später gegen Aufrufe linkt.

Bei Fragen bitte fragen, ich gehe auch gerne nochmal näher auf einzelne Dinge ein.
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden Yahoo Messenger MSN Messenger
Beiträge der letzten Zeit anzeigen:   
Neues Thema eröffnen   Neue Antwort erstellen    JLI Spieleprogrammierung Foren-Übersicht -> Entwicklung Alle Zeiten sind GMT
Seite 1 von 1

 
Gehe zu:  
Du kannst keine Beiträge in dieses Forum schreiben.
Du kannst auf Beiträge in diesem Forum nicht antworten.
Du kannst deine Beiträge in diesem Forum nicht bearbeiten.
Du kannst deine Beiträge in diesem Forum nicht löschen.
Du kannst an Umfragen in diesem Forum nicht mitmachen.


Powered by phpBB © 2001, 2005 phpBB Group
Deutsche Übersetzung von phpBB.de

Impressum