Når man erklærer noget i en header fil vil det typisk blive inkluderet af flere cpp filer. Det betyder at hvis man erklærer en variabel i en include fil med :
// x.h int Xint;
og derefter inkluderer x.h i to filer (a.cpp og b.cpp) vil Xint findes i både a.obj og b.obj når man har kompileret. Når man senere linker sit program vil linkeren klage over at Xint er defineret to gange.
Ved at erklære Xint i den ene af .cpp filerne sikrer man sig at den blive oprettet netop en gang.
Forskellen er om variablen er tilgængelig per instans af din klasse, eller som en enkelt global instans. Når du har en medlemsvariabel (private, protected eller public), så har du en variabel per instans. Når den samme variabel er global i din .cpp fil, så har du kun en af dem, uanset hvor mange instanser du har gang i. Det giver øget kompleksitet i et flertrådet miljø.
Forskellen mellem #include xxx i din header eller .cpp fil er at når du har #include xxx i din .h fil, så bliver indholdet af xxx kopieret ind i din .h fil. Når du har #include xxx i din .cpp fil, så bliver indholdet af xxx kopieret ind i din .cpp fil.
Generelt bør du lave dine .h filer stand-alone. Det vil sige, hvis du vil bruge funktionalitet du har defineret i en .h fil bør det kun være nødvendigt at #include den ene .h fil. Så hvis du bruger std::string i din .h fil, så skal du lave #include <string> i din .h fil.
Ved at lade en include file inkluderer andre include filer kan man lette brugen af include filen. F.eks.
//x.h #include "y.h"
class x { Y myY; }
Når man skal bruge x klassen behøver man kun inkludere x.h. Hvis man udelader at inkluderer y.h i x.h skal dem der bruger x.h selv huske at inkludere y.h - kompileren vil dog opmærksom på at der mangler noget.
Ulempen ved at lade inklude filer inkludere andre filer er at det kan være svært at overskue hvilken rækkefølge tingene kommer ind i. I projekter med mange source filer kan man også risikere at man får hentet den samme fil ind rigtigt mange gange, fordi man har header filer der inkluderer hinanden.
Så får du godt nok hentet det ind flere gange (idet præprocessoren er ganske uintelligent), men det bliver kun erklæret en gang, og belaster oversætteren minimalt.
Du skal lige være opmærksom på at olennert og mit svar er forskellige da vi har taget forskellige udgangspunkter - olennert antager at du erklærer din variabel inden i en klasse (class-scope) og jeg antager at du har erklæret den udenfor (fil-scope)
Med al respekt så synes jeg altså at der mangler et par betragtninger omkring erklæringen af variable udenfor klasser/metoder.
Løsningen på multiple definition er ikke at flytte fra .H til .C/.CPP.
Hvis den kun skal bruges i en enkelt .C/.CPP så erklærer man den i .C/.CPP med static altså: static int Xint; så er der ingen konflikter.
Hvis den skal bruges i flere .C/.CPP så skal den erklæres int Xint; 1 sted og: extern int Xint; alle de andre steder.
Der er forskellige måder at gøre det på. En er at erklære den med extern i en .H og den uden extern i en enkelt .C/.CPP som ikke inkluderer den .H. En anden er at erklære som følger: EXTERN int Xint; og så have #define EXTERN extern for alle .C/.CPP undtagen en som har: #define EXTERN
Jeg vil tillade mig at konkludere at C++ har kønnere måder at gøre det på end C.
arne, hvorfor erklærer du Xint static i det første eksempel? Hvis det er for at undgå at symbolet bliver eksporteret, så vil jeg foretrække anonymt namespace.
arne_v> Nu er du jo ikke en compiler, så check hvad der sker hvis du compiler en fil der indeholder :
extern int X; int X;
eller
int X; extern int X;
Sålænge de to erklæringer definerer X som den samme type er alt i den skønneste orden. Det er klart at hvis man laver en direkte og en extern erklæring hvor typerne ikke er de samme så vil kompileren give en fejl.
extern int X opretter et entry i symbol tabellen, med mindre der er et i forvejen.
int X erklæringen opretter et entry i symbol tabellen (med mindre der er et i forvejen) _og_ sørger for at der når den færdige eksekverbare bliver loadet bliver afsat plads til en integer.
Du skriver : "Den lider jo af samme problem som løsningen med include undtagen i en .C/.CPP." - det forstår jeg ikke ?
Jeg kan ikke se hvad der er for en afhængighed der ikke kan checkes af kompileren.
Og problemet jeg snakker om er, at du får 2 linier i 2 forskellige filer som skal have samme type og samme navn. Ellers så vil applikationen ikke virke korrekt. Og compileren vil ikke altid kunne fange det.
Hvis man bruger #define så står navn og type kun 1 gang i 1 fil.
"En er at erklære den med extern i en .H og den uden extern i en enkelt .C/.CPP som ikke inkluderer den .H. "
Det jeg ikke forstår er hvorfor du vil have den direkte erklæring (uden extern) í en .c/.cpp fil der ikke inkludere den .h fil der hvori extern erklæringen findes.
Linkeren giver måske fejl, fordi n ikke er defineret nogen steder.
Men det kan man ikke regne med.
MIMER::ARNE> cc/standard=ansi ext1 MIMER::ARNE> cc/standard=ansi ext2 MIMER::ARNE> link ext1+ext2 %LINK-W-NUDFSYMS, 1 undefined symbol: %LINK-I-UDFSYM, N %LINK-W-USEUNDEF, undefined symbol N referenced in psect $LINK$ offset %X00000020 in module EXT2 file SYS$SYSDEVICE:[USER.ARNE]EXT2.OBJ;5
MIMER::ARNE> cc/standard=ansi/extern=common ext1 MIMER::ARNE> cc/standard=ansi/extern=common ext2 MIMER::ARNE> link ext1+ext2 MIMER::ARNE> r ext1 n=0
ANSI C definerer ikke hvordan linkere skal håndtere den situation.
Ovenstående compiler har 2 måder at håndtere externe symboler på. Den ene giver linker fejl - den anden gør ikke.
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.