C++ #include

Rozwiązany

To, co naprawdę mi pomogło, to to, że mogłem # zawierać nagłówki w .plik cpp z out powodujący błąd redefinicji.


Jestem nowy w C++ , ale mam pewne doświadczenie w programowaniu w C# i Javie, więc może mi zabraknąć czegoś podstawowego, co jest unikalne dla C++.

Problem polega na tym, że nie bardzo wiem, co jest nie tak, wkleję jakiś kod, aby spróbować wyjaśnić problem.

Mam trzy klasy, GameEvents, fizykę i GameObject. Mam nagłówki dla każdego z oni. GameEvents ma jedną fizykę i listę obiektów GameObjects. Fizyka ma listę obiektów GameObjects.

Próbuję osiągnąć to, że chcę, aby GameObject mógł uzyskać dostęp lub posiadać obiekt fizyczny.

Jeśli po prostu #włączę " fizykę.h " W GameObject dostaję "error C2111: 'ClassXXX': 'class' type redifinition ' co rozumiem. I tu pomyślałem, że #include-guards pomoże, więc dodałem include guard do mojej fizyki.h ponieważ jest to nagłówek, który chcę zawrzeć dwa razy.

Tak to wygląda

#ifndef PHYSICS_H
#define PHYSICS_H

#include "GameObject.h"
#include <list>


class Physics
{
private:
    double gravity;
    list<GameObject*> objects;
    list<GameObject*>::iterator i;
public:
    Physics(void);
    void ApplyPhysics(GameObject*);
    void UpdatePhysics(int);
    bool RectangleIntersect(SDL_Rect, SDL_Rect);
    Vector2X CheckCollisions(Vector2X, GameObject*);
};

#endif // PHYSICS_H

Ale jeśli # włączę " fizykę.h " w mojej grze.h teraz tak:

#include "Texture2D.h"
#include "Vector2X.h"
#include <SDL.h>
#include "Physics.h"

class GameObject
{
private:
    SDL_Rect collisionBox;
public:
    Texture2D texture;
    Vector2X position;
    double gravityForce;
    int weight;
    bool isOnGround;
    GameObject(void);
    GameObject(Texture2D, Vector2X, int);
    void UpdateObject(int);
    void Draw(SDL_Surface*);
    void SetPosition(Vector2X);
    SDL_Rect GetCollisionBox();
};
Mam wiele problemów, które nie rozumieją, dlaczego się pojawiają. Jeśli nie # include " Fizyka.h " mój kod działa dobrze. Jestem bardzo wdzięczny za każdą pomoc.
Author: Shahbaz, 2011-11-05

6 answers

Preprocesor jest programem, który pobiera Twój program, wprowadza pewne zmiany (na przykład pliki nagłówkowe (#include), rozszerzenie makr (#define) i w zasadzie wszystko, co zaczyna się od #) i daje "czysty" wynik kompilatorowi.

Preprocesor działa tak, gdy widzi #include:

Kiedy piszesz:

#include "some_file"

Zawartość some_file prawie dosłownie zostaje wklejona do pliku wraz z nią. Teraz, jeśli mieć:

a.h:
class A { int a; };

I:

b.h:
#include "a.h"
class B { int b; };

I:

main.cpp:
#include "a.h"
#include "b.h"

Otrzymujesz:

main.cpp:
class A { int a; };  // From #include "a.h"
class A { int a; };  // From #include "b.h"
class B { int b; };  // From #include "b.h"

Teraz możesz zobaczyć, jak A jest redefiniowane.

Kiedy piszesz strażnicy, stają się tak:

a.h:
#ifndef A_H
#define A_H
class A { int a; };
#endif

b.h:
#ifndef B_H
#define B_H
#include "a.h"
class B { int b; };
#endif

Przyjrzyjmy się więc teraz, jak #includes W main będzie rozszerzony (jest to dokładnie tak, jak w poprzednim przypadku: kopiuj-wklej)

main.cpp:
// From #include "a.h"
#ifndef A_H
#define A_H
class A { int a; };
#endif
// From #include "b.h"
#ifndef B_H
#define B_H
#ifndef A_H          // From
#define A_H          // #include "a.h"
class A { int a; };  // inside
#endif               // "b.h"
class B { int b; };
#endif

Teraz podążajmy za preprocesorem i zobaczmy, jaki "prawdziwy" kod z tego wyjdzie. Pójdę linia po linii:

// From #include "a.h"

Skomentuj. Ignoruj! Dalej:

#ifndef A_H

Czy A_H jest zdefiniowany? Nie! Następnie kontynuuj:

#define A_H

Ok teraz A_H jest zdefiniowana. Dalej:

class A { int a; };

To nie jest coś dla preprocesora, więc zostaw to. Dalej:

#endif

Poprzedni if skończył się tutaj. Dalej:

// From #include "b.h"

Skomentuj. Ignoruj! Dalej:

#ifndef B_H

Czy B_H jest zdefiniowany? Nie! Następnie kontynuuj:

#define B_H

Ok teraz B_H jest zdefiniowana. Dalej:

#ifndef A_H          // From

Czy A_H jest zdefiniowany? Tak! Wtedy ignoruj do momentu odpowiadającego #endif:

#define A_H          // #include "a.h"

Ignoruj

class A { int a; };  // inside

Ignoruj

#endif               // "b.h"

Poprzedni if skończył się tutaj. Dalej:

class B { int b; };

To nie jest coś dla preprocesora, więc zostaw to. Dalej:

#endif

Poprzedni if skończył się tutaj.

To znaczy, po wykonaniu preprocesora z plikiem, to jest to, co widzi kompilator:

main.cpp
class A { int a; };
class B { int b; };

Więc jak widzisz, wszystko, co może dostać #include d w tym samym pliku dwa razy, bez względu na to, czy należy go strzec bezpośrednio czy pośrednio. Ponieważ pliki .h są zawsze bardzo prawdopodobne, że zostaną dołączone dwa razy, dobrze jest, jeśli strzeżysz wszystkich swoich .pliki H.

P. S. zauważ, że masz również okrągłe #includes. wyobraź sobie, że preprocesor kopiuje kod fizyki.h do GameObject.h, który widzi, że istnieje #include "GameObject.h", co oznacza kopię GameObject.h w sobie. Kiedy kopiujesz, znowu dostajesz #include "Pysics.h" i utknąłeś w pętli na zawsze. Kompilatory temu zapobiegają, ale to oznacza, że twoje #include s są połowa skończona.

Zanim powiesz, jak to naprawić, powinieneś wiedzieć coś innego.

Jeśli masz:

#include "b.h"

class A
{
    B b;
};

Następnie kompilator musi wiedzieć wszystko o b, co najważniejsze, jakie ma zmienne itp., aby wiedział, ile bajtów powinien umieścić w miejscu b w A.

Jednakże, jeśli masz:

class A
{
    B *b;
};

Wtedy kompilator tak naprawdę nie musi nic wiedzieć o B (ponieważ wskaźniki, niezależnie od typu mają takie same rozmiar). Jedyne, co musi wiedzieć o B, to to, że istnieje!

Więc robisz coś, co nazywa się "forward declaration":

class B;  // This line just says B exists

class A
{
    B *b;
};

Jest to bardzo podobne do wielu innych rzeczy, które robisz w plikach nagłówkowych, takich jak:

int function(int x);  // This is forward declaration

class A
{
public:
    void do_something(); // This is forward declaration
}
 97
Author: Shahbaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-10 20:39:09

Masz tutaj odniesienia okrągłe: Physics.h zawiera GameObject.h który zawiera Physics.h. Twoja klasa Physics używa GameObject* (pointer) type, więc nie musisz dołączać GameObject.h do Physics.h , ale po prostu użyj deklaracji forward-zamiast

#include "GameObject.h" 

Put

class GameObject;   

Ponadto umieść osłony w każdym pliku nagłówkowym.

 5
Author: Bojan Komazec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-05 12:26:46

Problem polega na tym, że twój GameObject.h nie ma strażników, więc kiedy #include "GameObject.h" w Physics.h zostanie uwzględniony, gdy GameObject.h zawiera Physics.h.

 4
Author: bitmask,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-05 12:19:23

Dodaj osłony include we wszystkich plikach nagłówkowych *.h LUB *.hh (chyba że masz konkretne powody, aby tego nie robić).

Aby zrozumieć, co się dzieje, spróbuj uzyskać wstępnie przetworzoną formę kodu źródłowego. Z GCC jest to coś w rodzaju g++ -Wall -C -E yourcode.cc > yourcode.i (nie mam pojęcia jak kompilatory Microsoftu to robią). Możesz również zapytać, które pliki są dołączone, z GCC jako g++ -Wall -H -c yourcode.cc

 4
Author: Basile Starynkevitch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-05 12:19:49

Po pierwsze musisz również włączyć strażników w gameobject, ale to nie jest prawdziwy problem

Jeśli coś innego zawiera fizykę.najpierw fizyka.h obejmuje gameobject.H, dostajesz coś takiego:

class GameObject {
...
};

#include physics.h

class Physics {
...
};

Oraz #include physics.h zostaje odrzucony z powodu strażników include, a Ty kończysz z deklaracją GameObject przed deklaracją fizyki.

Ale to jest problem, jeśli chcesz GameObject mieć wskaźnik do fizyki, ponieważ dla fizyki htat musiałby być zadeklarowany jako pierwszy.

Aby rozwiązać cykl, możesz zamiast tego zadeklarować klasę forward-declare, ale tylko wtedy, gdy używasz jej jako wskaźnika lub odniesienia w poniższej deklaracji, tj.:

#ifndef PHYSICS_H
#define PHYSICS_H

//  no need for this now #include "GameObject.h"

#include <list>

class GameObject;

class Physics
{
private:
    list<GameObject*> objects;
    list<GameObject*>::iterator i;
public:
    void ApplyPhysics(GameObject*);
    Vector2X CheckCollisions(Vector2X, GameObject*);
};

#endif // PHYSICS_H
 4
Author: je4d,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-05 12:27:34

Użyj include guards w WSZYSTKICH Twoich plikach nagłówkowych. Ponieważ używasz Visual Studio, możesz użyć #pragma once jako pierwszej definicji preprocesora we wszystkich nagłówkach.

Proponuję jednak zastosować podejście Klasyczne:

#ifndef CLASS_NAME_H_
#define CLASS_NAME_H_

// Header code here

#endif //CLASS_NAME_H_

Po drugie przeczytaj o deklaracji forward i zastosuj ją.

 3
Author: pnezis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-05 12:42:37