[Anfänger] Headerdatei: was gehört rein, was nicht?
-
Ich würde gerne meinen bestehenden Quelltext mal aufräumen und auch auf mehrere Quelltextdateien verteilen. Leider komme ich mit dem klar, was in meiner eventuellen main.h stehen müsste. In meiner greifbaren Literatur werden im Kapitel "Header-Dateien" nur Variablen behandelt...
Wie müsste ich den folgenden kompletten & funktionierenden Beispielcode aufteilen, wenn in der hier komplett gelisteten "main.cpp" quasi nur noch die main-Funktion stehen soll? Was sollte in einer "main.h", was in eine "subcode.cpp" bzw. welche Zeilen müssten dort genau hinzugefügt werden?
//main.cpp #include <iostream> #include <vector> class testklasse { public: std::vector<int> Testvector2; int testvariable; testklasse(std::vector<int> Testvector2, int testvariable); }; testklasse::testklasse(std::vector<int> Testvector2, int testvariable) { this->Testvector2=Testvector2; this->testvariable=testvariable; } void testfunktion(std::vector<testklasse>& Testvector1) { std::vector<int> tempvector; tempvector.push_back(12); tempvector.push_back(34); tempvector.push_back(56); int tempvariable = 99; Testvector1.push_back(testklasse(tempvector, tempvariable)); } void testausgabe(std::vector<testklasse>& Testvector1) { std::vector<testklasse>::iterator i; for (i = Testvector1.begin(); i < Testvector1.end(); i++) { std::vector<int>::iterator j; for (j = i->Testvector2.begin(); j < i->Testvector2.end(); j++) { std::cout << *j << std::endl; } } } int main() { std::vector<testklasse> Testvector1; testfunktion(Testvector1); testausgabe(Testvector1); return 0; }
-
Generell: Deklarationen in die Header-Datei, Definitionen in die cpp-Datei.
// subcode.hpp oder subcode.h #include <vector> class testklasse { public: std::vector<int> Testvector2; int testvariable; testklasse(std::vector<int> Testvector2, int testvariable); }; void testfunktion(std::vector<testklasse>& Testvector1); void testausgabe(std::vector<testklasse>& Testvector1);// subcode.cpp #include "subcode.hpp" #include <iostream> testklasse::testklasse(std::vector<int> Testvector2, int testvariable) : Testvector2(Testvector2) // Das ist eine Initialisierungsliste , testvariable(testvariable) { } void testfunktion(std::vector<testklasse>& Testvector1) { std::vector<int> tempvector; tempvector.push_back(12); tempvector.push_back(34); tempvector.push_back(56); int tempvariable = 99; Testvector1.push_back(testklasse(tempvector, tempvariable)); } void testausgabe(std::vector<testklasse>& Testvector1) { std::vector<testklasse>::iterator i; for (i = Testvector1.begin(); i < Testvector1.end(); i++) { std::vector<int>::iterator j; for (j = i->Testvector2.begin(); j < i->Testvector2.end(); j++) { std::cout << *j << std::endl; } } }//main.cpp #include "subcode.hpp" #include <vector> int main() { std::vector<testklasse> Testvector1; testfunktion(Testvector1); testausgabe(Testvector1); return 0; }
-
Ungetestet, aber ungefähr so:
testklasse.h:
#ifndef TESTKLASSE_H #define TESTKLASSE_H #include <vector> class testklasse { public: std::vector<int> Testvector2; int testvariable; testklasse(std::vector<int> Testvector2, int testvariable); }; void testfunktion(std::vector<testklasse>& Testvector1); void testausgabe(std::vector<testklasse>& Testvector1); #endiftestklasse.cpp:
#include <iostream> #include "testklasse.h" testklasse::testklasse(std::vector<int> Testvector2, int testvariable) { this->Testvector2=Testvector2; this->testvariable=testvariable; } void testfunktion(std::vector<testklasse>& Testvector1) { std::vector<int> tempvector; tempvector.push_back(12); tempvector.push_back(34); tempvector.push_back(56); int tempvariable = 99; Testvector1.push_back(testklasse(tempvector, tempvariable)); } void testausgabe(std::vector<testklasse>& Testvector1) { std::vector<testklasse>::iterator i; for (i = Testvector1.begin(); i < Testvector1.end(); i++) { std::vector<int>::iterator j; for (j = i->Testvector2.begin(); j < i->Testvector2.end(); j++) { std::cout << *j << std::endl; } } }main.cpp:
#include <vector> #include "testklasse.h" int main() { std::vector<testklasse> Testvector1; testfunktion(Testvector1); testausgabe(Testvector1); return 0; }edit: Oh, ich habe die Includeguards vergessen. Reineditiert.
-
Vielen Dank, wunderbar, das erklärt mir gut, wie das alles ausschauen muss.
Mein Hauptprojekt besteht nun genau wie der Beispielcode aus 3 Dateien:
main.cpp
battle.cpp
battle.hAber nun ist da noch 2 kleine Fragen:
a) Sollte ich sämtliche Includes von STL-Headern in #ifndef / #define / #endif setzen, um nicht alles x-mal zu inkludieren? Oder gibts da eine Automatik?
b) Ich möchte / muss für meine Klasse, die ja nun in einer Headerdatei (battle.h) definiert wird, eine globale Variable anlegen. Da gibts dann aber einen Error beim linken:1>main.obj : error LNK2005: "int global_id" (?global_id@@3HA) ist bereits in battle.obj definiert. 1>C:\Users\Tobias\Documents\Visual Studio 2010\Projects\rpg01\Debug\rpg01.exe : fatal error LNK1169: Mindestens ein mehrfach definiertes Symbol gefunden.Obwohl diese Variable wirklich nur in battle.h benutzt wird. Ich habe diese dann sogar mit
#ifndef __GLOBALE_ID__ #define __GLOBALE_ID__ int global_id = 1; #endifausgestattet. Gleicher Fehler. Was ist hier falsch? Besonderheiten bei globalen Variablen?
EDIT: Ich hab mal wieder Quatsch geschrieben, ich weiß. Die battle.h wird ja 2 mal in den cpp's inkludiert, deshalb der Fehler. Aber mit #ifndef sollte das doch behoben sein...
-
a) Sollte ich sämtliche Includes von STL-Headern in #ifndef / #define / #endif setzen, um nicht alles x-mal zu inkludieren? Oder gibts da eine Automatik?
Das ist nicht notwendig. Compiler können die typischen Includeguards erkennen und vermeiden es dann, die Header ein zweites Mal einzubinden (GCC jedenfalls). Beim MSVC kann man das mit
#pragma onceauch explizit unterbinden.Die battle.h wird ja 2 mal in den cpp's inkludiert, deshalb der Fehler. Aber mit #ifndef sollte das doch behoben sein...
Nein, ein #define gilt nur für den Rest der aktuellen Übersetzungseinheit.
Wie gesagt, in die Header gehören nur Deklarationen. In diesem Fall sollte global_id ein statisches Member deiner Klasse sein, und definiert wird sie dann in battle.cpp.
-
thuering schrieb:
a) Sollte ich sämtliche Includes von STL-Headern in #ifndef / #define / #endif setzen, um nicht alles x-mal zu inkludieren? Oder gibts da eine Automatik?
Nicht nur um die STL-Header. Ein Include-Guard gehört um den gesamten Inhalt des Headers herum (btw, die Standard-Header dürften sich in der Hinsicht schon selber schützen).
b) Ich möchte / muss für meine Klasse, die ja nun in einer Headerdatei (battle.h) definiert wird, eine globale Variable anlegen. Da gibts dann aber einen Error beim linken:
[...]
Gleicher Fehler. Was ist hier falsch? Besonderheiten bei globalen Variablen?
Nein, für Variablen gelten die selben Regeln, die fdfdg schon genannt hat - in den Header gehört nur eine Deklaration (
extern int global_id;), die Definition sollte in einer CPP-Datei landen.
-
Danke, läuft.
Das "extern" hatte ich sogar auch schon rausgefunden
Momentan arbeite ich mit MSVC 2010 Express - wenn ich jetzt "#pragma once" einbaue und später auf gcc umsteige, gibts dann damit Probleme?
-
GCC unterstüzt es soweit ich weiß, aber es gibt keinen Grund das zu verwenden, da nicht standardkonform. Mach einfach folgendes:
#ifndef MYHEADER_HPP #define MYHEADER_HPP // quellcode #endif // MYHEADER_HPP
-
GCC kennt wenn ich mich nicht täusche #pragma once nicht. Aber warum willst du eigentlich von VC auf GCC umsteigen?
-
Jonas OSDever schrieb:
GCC kennt wenn ich mich nicht täusche #pragma once nicht.
Dachte ich zwar auch, aber scheinbar kann er das schon länger.
Es spricht auch nichts dagegen, beides zu verwenden. Die normalen Includeguards sind Pflicht, aber ein zusätzliches #pragma once davor schadet nicht.
Ein Compiler, der #pragma once nicht kennt, muss es ignorieren.
-
Wenn man sich fragt, was in eine Header-Datei gehört und was nicht, hat man wahrscheinlich die one-definition-rule nicht verinnerlicht. Das ist eine "große" (mehr als ein Satz) und wichtige Regel, die man kennen sollte. Darüber hinaus, sollte man so wenig wie möglich im Header verraten, um Abhängigkeiten zu reduzieren. Der Rest ergibt sich mit gesundem Menschenverstand.
-
-
OK, #ifndef und #pragma once werde ich beide verwenden. Zu gcc werde ich sicherlich demnächst wechseln, da ich Plattformabhängig programmieren möchte und mein Progrämmchen dann auch gerne direkt unter LiNNux weiterentwickle. Außerdem habe ich in diesem Forum schon des öfteren gelesen, gcc sei der einzige ernsthafte Compiler...

wxSkip: Vielen Dank für diesen äußerst informativen Link / Post!
-
thuering schrieb:
OK, #ifndef und #pragma once werde ich beide verwenden. Zu gcc werde ich sicherlich demnächst wechseln, da ich Plattformabhängig programmieren möchte und mein Progrämmchen dann auch gerne direkt unter LiNNux weiterentwickle. Außerdem habe ich in diesem Forum schon des öfteren gelesen, gcc sei der einzige ernsthafte Compiler...

wxSkip: Vielen Dank für diesen äußerst informativen Link / Post!
Keine Ursache, verlinke ich immer

Und das mit dem ernsthaften Compiler hast du immer von Pi gelesen, oder?
-
Ach, ich lese gerade, ich möchte natürlich plattformUNabhängig programmieren

Ich hatte das Gefühl, den Spruch über gcc hier von mehreren Personen zu lesen...Pi == 314159265358979 ? http://www.c-plusplus.net/forum/u115511
-
Ja, ich bin ich. Oder so.
Und für fortgeschrittenes C++ ist der GCC der bessere Compiler.
Er ist, was templates angeht, extrem standardkonform, MSVC führt da irgendwas komisches auf, offenbar kein Königs-Lookup.Wie auch immer. MSVC ist für dich momentan (noch) gut genug. Der Debugger von MSVC ist übrigens wirklich super.
-
314159265358979 schrieb:
Wie auch immer. MSVC ist für dich momentan (noch) gut genug.
Na Danke! Toll, wie Du meine weltweit beachteten Programmierleistungen schätzt... *hust*

314159265358979 schrieb:
Der Debugger von MSVC ist übrigens wirklich super.
Als wenn ich schon SO WEIT wäre, dass ich den Debugger auch nur ein mal schon benutzt hätte...
Ich brauche weder MSVC noch gcc. Ich programmiere mir heute Abend meinen eigenen C++ Compiler. In Assembler und Ada. Programmiersprachen für echte Männer halt.
:pMal im Ernst: Ich will demnächst wirklich weg von Windows; Ich würde das gerne nur noch in Ausnahmefällen als VM starten. Ich bin schon lange großer openSUSE- aber auch Solaris-Fan (was Serversysteme angeht). Für beide Systeme gibt es Eclipse, welches anscheinend mit ordentlich Rückenwind aus der Industrie vorangetrieben wird. Wie ist denn hier die Meinung zu Eclipse?
-
Ich benutze die Eclipse selbst ... und bin damit wohl der einzige hier

-
Zufallszahlen mit Minimum / Maximumwert
Die Syntax zur Erzeugung von Zufallszahlen ist mir bekannt. Nur stehe ich gerade auf dem Mathe-Schlauch, wie ich Zufallszahlen in einem bestimmten Zahlenbereich erzeuge.Das hier
int Wuerfeln(int min, int max) { int zufallszahl = rand() % max + min; return zufallszahl; }ist ja mathematisch falsch, denn so kann das Maximum überschritten werden.
Ich hab wieder Würmer im Hirn...
-
Dann zieh doch vorher das Minimum vom Maximum ab!
-
Jaaaa! Jaaaa! Das ist es! Warum nicht gleich?! Naja, es ist schon spät.