Aufteilung des Sourcecodes in mehrere Dateien (+Header)



  • Hallo zusammen!

    Ich programmiere nun schon eine ganze Weile, aber mit einer Sache komme ich seit Jahren nicht zurecht, wenn ich mit C / C++ programmiere!!

    Es geht um die Aufteilung eines Quelltextes in mehrere *.cpp Dateien inklusive Header-Dateien.

    Solange ich all meinen code in einer Datei (üblicherweise main.cpp) schreibe, habe ich selten größere Schwierigkeiten. Aber sobald ich es auch nur wage, eine weitere Datei hinzuzufügen, hagelt es an nichtssagenden Fehlermeldungen.

    Mein Sourcecode in einer Datei wie folgt aus:
    (Bitte nicht abschrecken lassen! Ist nur zur vollständigkeit hier)

    #include "SDL.h" 
    #include "SDL_image.h" 
    #include "SDL_mixer.h" 
    #include "SDL_ttf.h" 
    #include <string> 
    #include <fstream>
    #include <cstdio>
    
    using namespace std;
    
    //The attributes of the screen
    const int SCREEN_WIDTH	= 800;
    const int SCREEN_HEIGHT = 600;
    const int SCREEN_BPP	= 32;
    
    //The surfaces that will be used
    SDL_Surface *pos		 = NULL;
    SDL_Surface *message	 = NULL;
    SDL_Surface *title		 = NULL;
    SDL_Surface *background	 = NULL;
    SDL_Surface *screen		 = NULL;
    
    //The font that's going to be used
    TTF_Font *font = NULL;
    
    //The color of the font
    SDL_Color textColor = {255, 255, 255};
    
    //The event structure that will be used 
    SDL_Event event; 
    
    //The music that will be played
    Mix_Music *music = NULL; 
    
    ////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    bool write_log(string text, bool newline = false) {
    	// Open file handler
    	fstream file;
    
    	// Create / overwrite the current log file
        file.open("log.txt",  ios::out | ios::app);
    
    	// Write down the message
        file << text;
    
    	if(newline == true) {
    		file << endl;
    	}
    
        file.close();
    
    	return true;
    }
    
    SDL_Surface *load_image(string filename) {
    	// Write log file
    	write_log("Loading image \"" + filename + "\"... ");
    
        // The image that's loaded
        SDL_Surface* loadedImage = NULL;
    
        // The optimized image that will be used
        SDL_Surface* optimizedImage = NULL;
    
        // Load the image using SDL_image
        loadedImage = IMG_Load(filename.c_str());
    
        // If the image loaded
        if(loadedImage != NULL){
    
            // Create an optimized image
            optimizedImage = SDL_DisplayFormat(loadedImage);
    
    		//Map the color key (white)
    		Uint32 colorkey = SDL_MapRGB(optimizedImage->format, 0xFF, 0xFF, 0xFF); 
    
    		//Set all pixels of color R 0xFF, G 0xFF, B 0xFF to be transparent
    		SDL_SetColorKey(optimizedImage, SDL_SRCCOLORKEY, colorkey); 
    
            // Free the old image
            SDL_FreeSurface(loadedImage);
        }
    
    	// Write log file
    	write_log("done", true);
    
        // Return the optimized image
        return optimizedImage;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    void apply_surface(int x, int y, SDL_Surface* source, SDL_Surface* destination) {
        // Make a temporary rectangle to hold the offsets
        SDL_Rect offset;
    
        // Give the offsets to the rectangle
        offset.x = x;
        offset.y = y;
    
    	// Blit the surface
    	SDL_BlitSurface(source, NULL, destination, &offset); 
    } 
    
    ////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    bool init() {
    	// Write log file
    	write_log("Initializing SDL subsystems... ");
    
        // Initialize all SDL subsystems
        if(SDL_Init( SDL_INIT_EVERYTHING ) == -1) {
    		write_log("error", true);
            return false;    
        }
    
    	// Write log file
    	write_log("done", true);
    
    	// Write log file
    	write_log("Initializing SDL_TTF... ");
    
    	//Initialize SDL_ttf
        if(TTF_Init() == -1) {
    		// Write log file
    		write_log("error", true);
            return false;    
        }
    
    	// Write log file
    	write_log("done", true);
    
    	// Write log file
    	write_log("Setting video mode to 800x600... ");
    
        // Set up the screen
        screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE);
    
        // If there was an error in setting up the screen
        if(screen == NULL) {
    		// Write log file
    		write_log("error", true);
            return false;    
        }
    
    	// Write log file
    	write_log("done", true);
    
    	// Write log file
    	write_log("Initializing SDL_Mixer... ");
    
    	//Initialize SDL_mixer 
    	if(Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 4096) == -1) {
    		// Write log file
    		write_log("error", true);
    		return false; 
    	} 
    
    	// Write log file
    	write_log("done", true);
    
    	// Write log file
    	write_log("Setting window title... ");
    
        // Set the window caption
        SDL_WM_SetCaption("Black Orbit 2", NULL);
    
    	// Write log file
    	write_log("done", true);
    
        // If everything initialized fine
        return true;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    bool load_files() {
        // Load the image
        pos			= load_image("Data\\x.png");
    	title		= load_image("Data\\blackorbit.png");
    	background	= load_image("Data\\bg.png"); 
    
        // If there was an error in loading the image
        if(pos == NULL) {
            return false;    
        }
    
    	// If there was an error in loading the image
        if(background == NULL) {
            return false;    
        }
    
    	// If there was an error in loading the image
        if(title == NULL) {
            return false;    
        }
    
    	// Write log file
    	write_log("Opening font file... ");
    
    	// Open the font 
    	font = TTF_OpenFont("Data\\LiberationSans-Bold.ttf", 13); 
    
    	// If there was an error in loading the font 
    	if(font == NULL) {
    		return false; 
    		// Write log file
    		write_log("error", true);
    	} 
    
    	// Write log file
    	write_log("done", true);
    
    	// Write log file
    	write_log("Loading music... ");
    
    	// Load the music
    	music = Mix_LoadMUS("Data\\beat.wav"); 
    
    	// If there was a problem loading the music
    	if(music == NULL) {
    		// Write log file
    		write_log("error", true);
    		return false;
    	} 
    
    	// Write log file
    	write_log("done", true);
    
        // If everything loaded fine
        return true;    
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    void clean_up() {
    
    	// Write log file
    	write_log("Cleaning surfaces... ");
    
        // Free the image
        SDL_FreeSurface(pos);
    	SDL_FreeSurface(background);
    	SDL_FreeSurface(message);
    	SDL_FreeSurface(title);
    
    	// Write log file
    	write_log("done", true);
    
    	// Write log file
    	write_log("Closing font... ");
    
    	// Close the font that was used
    	TTF_CloseFont(font);
    
    	// Write log file
    	write_log("done", true);
    
    	// Write log file
    	write_log("Quitting SDL_TTF... ");
    
    	// Quit SDL_ttf 
    	TTF_Quit(); 
    
    	// Write log file
    	write_log("done", true);
    
    	// Write log file
    	write_log("Cleaning music buffer... ");
    
    	// Free the sound effects
    	Mix_FreeMusic(music); 
    
    	// Write log file
    	write_log("done", true);
    
    	// Write log file
    	write_log("Quitting SDL_Mixer... ");
    
    	//Quit SDL_mixer
    	Mix_CloseAudio(); 
    
    	// Write log file
    	write_log("done", true);
    
    	// Write log file
    	write_log("Quitting SDL... ");
    
        // Quit SDL
        SDL_Quit();    
    
    	// Write log file
    	write_log("done", true);
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    int main(int argc, char* args[]) {
    	// Delete the old log file
    	remove("log.txt");
    
    	// Make sure the program waits for a quit
        bool quit = false;
    
    	// Initialize
        if(init() == false) {
            return 1;    
        }
    
        // Load the files
        if(load_files() == false) {
            return 1;    
        }
    
    	// Render the text
        message = TTF_RenderText_Solid(font, "Dies ist eine Testnachricht.", textColor);
    
        // If there was an error in rendering the text
        if(message == NULL) {
            return 1;    
        }
    
    	int x_pos_pointer = 0;
    	int y_pos_pointer = 0;
    	int x_pos_pointer_max = 800 - 32;
    	int y_pos_pointer_max = 600 - 32;
    	int x_pos_pointer_min = 0;
    	int y_pos_pointer_min = 0;
    
    	int x_pos_title = 200;
    	int x_pos_title_max = 800;
    	int x_pos_title_min = -400;
    	int x_pos_title_speed = 3;
    
    	// Get the keystates 
    	Uint8 *keystates = SDL_GetKeyState(NULL); 
    
    	// While the user hasn't quit 
    	while( quit == false) { 
    		if(keystates[ SDLK_UP ]) {
    			if(y_pos_pointer >= y_pos_pointer_min) {
    				y_pos_pointer--;
    			}
    		}
    
    		if(keystates[ SDLK_DOWN ]) {
    			if(y_pos_pointer <= y_pos_pointer_max) {
    				y_pos_pointer++;
    			}
    		}
    
    		if(keystates[ SDLK_LEFT ]) {
    			if(x_pos_pointer >= x_pos_pointer_min) {
    				x_pos_pointer--;
    			}
    		}
    
    		if(keystates[ SDLK_RIGHT ]) {
    			if(x_pos_pointer <= x_pos_pointer_max) {
    				x_pos_pointer++;
    			}
    		}
    
    		// While there's an event to handle
    		while(SDL_PollEvent(&event)) {            
    			// If the user has Xed out the window
    			if (event.type == SDL_QUIT) {
                    // Quit the program
                    quit = true;
                }    
            }
    
    		// Apply the background to the screen
    		apply_surface(0, 0, background, screen); 
    
    		// Apply the message to the screen 
    		apply_surface(200, 50, message, screen); 
    
    		// Apply the pos to the screen 
    		apply_surface(x_pos_pointer, y_pos_pointer, pos, screen); 
    
    		// Apply the title to the screen 
    		apply_surface(x_pos_title, 200, title, screen); 
    
    		x_pos_title += x_pos_title_speed;
    
    		if(x_pos_title >= x_pos_title_max) {
    			x_pos_title = x_pos_title_min;
    		}
    
    		// If there is no music playing
            if(Mix_PlayingMusic() == 0) {
                // Play the music
                if(Mix_PlayMusic(music, -1) == -1) {
                    return 1;
                }    
            }
    
    		// Update the screen 
    		if(SDL_Flip(screen) == -1) {
    			return 1; 
    		} 
    
    		SDL_Delay(1);
        }
    
    	// Free the surface and quit SDL 
    	clean_up(); 
    
    	// Return 
    	return 0; 
    }
    

    Ich habe nun versucht (!!) ganz simpel erst einmal eine Funktion nach der anderen in andere Dateien auszulegern.

    Die system.cpp sieht im Moment so aus:

    #include <string>
    #include <fstream>
    #include <cstdio>
    #include "system.h"
    
    using namespace std;
    
    bool write_log(string text, bool newline = false) {
    	// Open file handler
    	fstream file;
    
    	// Create / overwrite the current log file
    	file.open("log.txt",  ios::out | ios::app);
    
    	// Write down the message
    	file << text;
    
    	if(newline == true) {
    		file << endl;
    	}
    
    	file.close();
    
    	return true;
    }
    

    Begonnen habe ich mit der Funktion write_log() (erste Funktion, ganz oben). Diese sollte in die neu angelegte Datei system.cpp.
    Den passenden Header habe ich dazu auch geschaffen um dort den Funktionsprototypen unterzubringen.

    Die system.h sieht dazu so aus:

    #ifndef SYSTEM_H
    #define SYSTEM_H
    
    	using namespace std;
    
    	bool write_log(string, bool = false)
    
    #endif
    

    Beim Testlauf ging natürlich wieder gar nix.

    Was mache ich falsch? Ich versuche immer alle benötigten Komponenten mittels #include einzufügen, achte auf den Namespace usw...

    Ständig haut mir der Compiler (und Linker) einen Batzen an Fehlermeldungen raus, aus denen ich absolut nicht schlau werde.

    Das raubt mir jeglichen Spaß und ist zudem auch verdammt frustrierend. 😡

    Kann mir jemand anhand des Beispiels system.cpp / system.h verdeutlichen wie man es nun macht? (Mir geht es erstmal nur um die Funktion write_log())

    PS: Ich habe mein Projekt zur Hilfe einfach mal hochgeladen.
    Ich verwende SDL, SDL_mixer, SDL_ttf und SDL_image. Ich denke, jemand der das Problem zu lösen vermag, weiß auch, wie damit umzugehen.

    Download des Projekts: http://thief.ac-host.net/_files/BlackOrbit2Help.zip

    Vielen Dank schon mal an jeden der sich mein Problem annimmt! 🙂



  • thief1064 schrieb:

    Die system.h sieht dazu so aus:

    #ifndef SYSTEM_H
    #define SYSTEM_H
    
    	using namespace std;
    
    	bool write_log(string, bool = false)
    
    #endif
    

    Mit dem Namen string kann der Compiler hier natürlich nichts anfangen. Du musst <string> inkludieren oder eine Vorwärtsdeklaration machen. Schau dir dazu mal http://www.drakon.ch/?id=&offset=&mobile=0&show_entry=77 an.



  • Du hast ein Semikolon am ende der Funktion in der Header vergessen. Noch als Tipp using namespace std nicht verwenden.



  • Hallo nochmal!

    VIELEN DANK!

    Ich habe <string> in system.h inkludiert und das fehlende Semikolon angehangen ( 🙄 ) und es funktioniert. Ich dachte allerdings, es wäre eine Art NO-GO innerhalb eines Headers andere Header zu inkludieren.

    Das mit der Vorwärtsdeklarations kannte ich in dem Sinne noch nicht, scheint aber logisch zu sein.

    Da mein Ursprünglicher Quelltext (main) ja noch ein ganzen Stück weiter aufgeteilt werden soll, rechne ich einfach mal damit, dass ich bald wieder so ein Problem bekomme....

    Ich fasse deswegen mal zusammen wie ich vorgehen würde:

    - Neue Datei mit Header erstellen
    - Funktion in die *.cpp und Prototyp, Variablen, etc in die *.h
    - xxx.h in die xxx.cpp inkludieren lassen
    - Alle benötigten Header der Funktionen innerhalb der xxx.h inkludieren (auch in die xxx.cpp???)
    - Fertig?

    Danke nochmal für die Hilfe! 🙂



  • thief1064 schrieb:

    Ich dachte allerdings, es wäre eine Art NO-GO innerhalb eines Headers andere Header zu inkludieren.

    Vielleicht hast du das ja im Zusammenhang mit using namespace std; gehört. Man versucht allerdings relativ wenige (d. h. die nötigen) Header in Headern einzubinden, weil es die Zeit zum Erstellen des Programms erheblich beeinflussen kann.

    Ich fasse deswegen mal zusammen wie ich vorgehen würde:

    - Neue Datei mit Header erstellen
    - Funktion in die *.cpp und Prototyp, Variablen, etc in die *.h
    - xxx.h in die xxx.cpp inkludieren lassen
    - Alle benötigten Header der Funktionen innerhalb der xxx.h inkludieren (auch in die xxx.cpp???)
    - Fertig?

    Vom groben Ablauf passt das. Allerdings solltest du keine globalen Variablen haben und die benötigten Header erst da inkludieren, wo sie auch gebraucht werden. Wenn eine Implementierung in der cpp-Datei einen Header braucht, die dazugehörige h-Datei allerdings nicht, sollte der Header natürlich erst in der cpp-Datei eingebunden werden.



  • Das mit dem NO-Go habe ich scheinbar wirklich verwechselt. Habe es nämlich vorhin schon mal im Zusammenhang mit using namespace gelesen.

    Ich bin gerade dabei eine weitere Funktion auszulagern und bin nun auf das Problem gestoßen, dass ich eben globale Variablen verwende, was nach dem Schema ja nicht möglich ist (oder doch?).

    Jedenfalls zeigt mir der Linker jetzt immer die Meldung an, dass meine globalen Variablen bereits definiert wurden.

    error LNK2005: "union SDL_Event event" (?event@@3TSDL_Event@@A) already defined in main.obj
    

    Die globals.h (Header mit globalen Variablen) wird nämlich zur Zeit sowohl in main.cpp als auch in system.cpp / system.h inkludiert.

    Was macht man da am besten?



  • Dafür gibts das Schlüsselwort extern . Aber wie gesagt solltest du eher die globalen Variablen loswerden.



  • Naja, ich habs jetzt aufgegeben.
    Ich spar mir lieber den Frust mit den verdammten Linker Fehlern, weil irgendwo wieder was nicht inkludiert wurde (oder zu viel) und realisier das Projekt lieber in C#.



  • thief1064 schrieb:

    Naja, ich habs jetzt aufgegeben.
    Ich spar mir lieber den Frust mit den verdammten Linker Fehlern, weil irgendwo wieder was nicht inkludiert wurde (oder zu viel) und realisier das Projekt lieber in C#.

    Das würde ich nicht so machen. Es kostet ein bisschen Aufwand, zu verstehen, was da eigentlich passiert, aber dann sollte alles klar sein.

    Mal ein paar Regeln:
    1.1 Beim Includen von Headern (*.h, .hpp) wird lediglich der Inhalt Buchstabe-für-Buchstabe vom Compiler in eine C++-Datei (.cpp, *.cxx, *.cc) eingefügt.
    1.2 Wenn der Compiler wissen will, welchen Typ eine Variable/Funktion hat, muss man die Variable vorher deklarieren bzw. die Funktion vorher deklarieren oder implementieren oder beides. Damit man das dem Compiler nicht für jede Datei neu mitteilen muss, kann man die ganzen "Bekanntmachungen" in den Header auslagern. Außerdem muss man dadurch die Bekanntmachung nur einmal ändern, wenn man den Typ der tatsächlichen Variable/Funktion ändert.

    2. Funktionen darf man nur einmal definieren (also implementieren) und Variablen nur einmal deklarieren, ansonsten sind sie für den Linker in mehreren Objektdateien, die aus den C++-Dateien erstellt werden, vorhanden und das kann er nicht verarbeiten, da er sich nun mal nicht entscheiden kann. 😉
    => Man sollte Funktionsdefinitionen und Variablendeklarationen NUR IN C++-DATEIEN MACHEN.
    ---- Außer: man macht Funktionen/Variablen static, dann sind sie immer nur in einer C++-Datei gültig. Dazu zählen auch inline-Funktionen, die automatisch static sind. Eine andere Ausnahme sind const-Variablen.

    3. Will man dennoch im Header klar stellen, dass die Funktion/Variable existiert, muss man
    3.1 Funktionen deklarieren (Bsp. "void print_something();")
    3.2 Variablen als extern deklarieren (Bsp. "extern int error_count"). Das sagt dem Compiler, dass die Variable "irgendwo anders", also extern definiert und damit ihr Speicherbereich reserviert ist. Man kann aber trotzdem eine extern-Deklaration und eine Deklaration einer Variablen in der selben Datei haben.

    4. Template-Funktionen sind, sofern sie nicht vollständig spezialisiert werden, static und damit im Header implementierbar. Wenn sie dennoch in einer C++-Datei implementiert werden, sind sie auch nur für diese Datei gültig, da helfen Deklarationen auch nichts. Der Grund ist, dass der Compiler beim kompilieren einer C++-Datei die Template-Funktion für jeden unterschiedlichen Template-Typ neu anlegen muss. Beispiel:

    template<typename T> T add(T const &first, T const &second)
    {
        return first + second;
    }
    

    Da diese Funktion bei jedem Typ einen anderen Binärcode erzeugt, muss sie der Compiler bei jedem Funktionsaufruf neu anlegen. Dabei kann er innerhalb einer C++-Datei Funktionen mit gleichem Typ, die mehrfach aufgerufen werden, nur einmal anlegen, was aber innerhalb der anderen C++-Dateien passiert, ist ihm nicht bekannt. Daher können diese angelegten Funktionen nur für eine Datei gelten, weil es sonst mehrfache Definitionen gäbe, falls die Funktion in mehreren Dateien für den gleichen Typ angelegt würde. Ist die Template-Funktion jetzt in Datei 1 implementiert und wird in Datei 2 aufgerufen, weiß der Compiler nicht, ob sie in Datei 1 für diesen Typ angelegt ist. Wenn aber nur die Deklaration vorhanden ist, kann er sie nicht neu anlegen, daher muss er einen Fehler ausgeben.
    Template-Funktionen sollten also entweder direkt im Header implementiert/definiert werden, oder (wie auch oft praktiziert) in einer ".impl"-Datei, die dann im Header inkludiert wird.

    Als Gegenbeispiel dazu sieht eine voll spezialisierte Template-Funktion so aus:

    template<typename T> class mytype  //Template-Klasse, muss in die Headerdatei
    {
        public:
        static T add(T const &first, T const &second)  //statische Funktion, Aufruf z.B.: "mytype<double>::add(1.0, 2.0)"
        {
            cout << "Int specialization\n";
            return first + second;
        }
    };
    
    template<> int mytype<int>::add(int const &first, int const &second)  //Spezialisierung, muss in die C++-Datei, Deklaration in die Headerdatei
    {
        return first + second;
    }
    

    5. Damit Klassen Pointer auf andere Klassen verwenden können, muss bekannt sein, dass es diese andere Klasse gibt.
    Das kann in dieser Form passieren

    class myclass
    {
        //private Variablen/Funktionsdeklarationen
    public:
        //öffentliche Funktionsdeklarationen/Variablen
    };
    

    Enthalten aber zwei Klassen Pointer auf Objekte der jeweils anderen, ist eine Vorwärtsdeklaration nötig:

    class B;
    
    class A
    {
        B *b;
    public:
        //...
    };
    
    class B
    {
        A *a;
    public:
        //...
    };
    

    Das gilt nur für Pointer und Referenzen, aber nicht für normale Objekte, mit denen so etwas aus gutem Grund nicht geht (ansonsten würden die Objekte, die auch noch andere Variablen enthalten, unendlich groß werden).

    Grundsätzlich kann man sich merken: Wenn der Compiler die Größe oder den Aufbau einer Variable (des Typs) wissen muss, reicht eine Vorwärtsdeklaration nicht aus.

    6. Man muss vermeiden, dass Header sich gegenseitig inkludieren. Ansonsten würde wie bei Klassen, die ein Objekt von der jeweils anderen Klasse enthalten, die Header unendlich oft inkludiert werden. Um zusätzlich zu vermeiden, dass Header mehrfach inkludiert werden, sollte man Header Guards verwenden:

    Utils.h:

    #ifndef UTILS_H  //wenn es nicht definiert ist, wurde der Header vorher noch nicht inkludiert
    #define UTILS_H //jetzt ist es aber definiert, d.h. der Header wird nicht nochmal inkludiert
    
    #include <string>
    
    class MyUTFString
    {
        //...
    };
    
    #endif  //UTILS_H
    

    Strings.h:

    #ifndef STRINGS_H
    #define STRINGS_H
    
    #include "Utils.h"
    
    //Funktionen, die UTFString benutzen
    
    #endif //STRINGS_H
    

    Numbers.h:

    #ifndef NUMBERS_H
    #define NUMBERS_H
    
    #include "Utils.h"
    
    //Funktionen, die UTFString benutzen
    
    #endif //NUMBERS_H
    

    main.cpp:

    #include "Strings.h"  //Utils.h wird inkludiert
    #include "Numbers.h"  //Utils.h wird dank der Include Guards nicht nochmal inkludiert
    
    int main()
    {
        //...
    }
    

    7.1 Membervariablen in Klassen dürfen im Header deklariert sein, da sie beim Ausführen des Programms nicht einfach global auf dem Stack angelegt werden, sondern nur darstellen, wie der Speicher einer Klasse interpretiert werden soll.
    Das heißt:

    class ABC
    {
        int x;    //die Variablendeklarationen hier dürfen und sollten in den Header
        string y;
    public:
        //...
    }
    

    7.2 Funktionen dürfen auch direkt in der normalen Klassendeklaration implementiert werden, dann sind sie automatisch inline und somit auch static. Dieses Vorgehen ist jedoch nur bei Template-Funktionen und eventuell bei Laufzeitkritischen Funktionen ratsam.

    Beispiel:

    class ABC
    {
        int tmp;
    public:
        void Compute_tmp_value(int param);  //berechnet den Wert von tmp, nicht Laufzeitkritisch, wird nur einmal benutzt
        template<typename T> void Assign_Pointer_Value(T *in)  //Template-Funktion, kommt in die Klassendeklaration im Header
        {
            tmp = reinterpret_cast<int>(in);
        }
        int GetAdded(int in)  //kurz und daher lohnt sich ein extra Aufruf weniger -> inline in der Klassendeklaration
        {
            return tmp + in;
        }
    };
    

    Für Templatefunktionen kann man auch alternativ folgende Syntax wählen:

    template<typename NumberType> class ABC
    {
        NumberType tmp;
    public:
        template<typename T> void Assign_Pointer_Value(T *in);
    };
    
    template<typename NumberType, typename T> void ABC<NumberType>::Assign_Pointer_Value(T *in)
    {
        tmp = reintepret_cast<int>(in);
    }
    

    7.3 statische Membervariablen in Klassen müssen in C++-Dateien nochmals angelegt werden, da sie sich wie globale Variablen verhalten und ihren eigenen Speicher auf dem Stack benötigen.

    Beispiel:
    Header:

    class ABC
    {
    public:
        static int abc;
    };
    

    C++-Datei:

    int ABC::abc = 5;
    

    8. Enumerations kommen in den Header, da ihre Werte bereits zur Compilezeit bekannt sind und so einfach wie bei #defines im Sourcecode verwendet werden können.

    Beispiel:

    //Header
    
    enum Essen
    {
        ESSEN_NICHTS = 0,
        ESSEN_BROT,
        ESSEN_GEMUESE,
        ESSEN_OBST,
        ESSEN_ANDERS
    };
    


  • Kommt das eigentlich mal in den FAQ?



  • Kleine Frage: Ist es nicht schöner, Makro statt #define zu sagen? 🙄



  • Hacker schrieb:

    Kleine Frage: Ist es nicht schöner, Makro statt #define zu sagen? 🙄

    Natürlich, aber vielleicht wissen manche nicht, was ein Makro ist. Bei #define ist es hingegen relativ klar.


Anmelden zum Antworten