c++ CPP和SDL2中的多重定义错误和未定义引用错误

ryoqjall  于 2023-02-06  发布在  其他
关注(0)|答案(1)|浏览(99)

我一直在尝试用SDL做一个简单的游戏。我发现把所有的东西都保存在文件中会变得很乱,所以我把一些函数移到了它们自己的文件中,并设置了一个全局头文件来存储我会使用的所有全局东西。但从那里开始,所有的错误都出现了!
文件:

-Main.cpp
-Makefile
-Assets/
  -Player.bmp
-Build/
  -build.deb
-Modules/
  -Global.h
  -HandleKeys.cpp
  -HandleKeys.h

Main.cpp:

#include <stdio.h>
#include "Modules/Global.h"
#include "Modules/HandleKeys.h"

//Starts up SDL and creates window
bool init();

//Loads media
bool loadMedia();

extern int SCREEN_WIDTH = 640;
extern int SCREEN_HEIGHT = 480;

//Frees media and shuts down SDL
void close();

//Loads individual image
SDL_Surface* loadSurface( std::string path );

//The window we'll be rendering to
SDL_Window* gWindow = NULL;
    
//The surface contained by the window
SDL_Surface* gScreenSurface = NULL;

//The images that correspond to a keypress
SDL_Surface* Entity[ KEY_PRESS_SURFACE_TOTAL ];

//Current displayed image
SDL_Surface* gCurrentSurface = NULL;

int PlayerX;
int PlayerY;

bool init()
{
    //Initialization flag
    bool success = true;

    //Initialize SDL
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() );
        success = false;
    }
    else
    {
        //Create window
        gWindow = SDL_CreateWindow( title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
        if( gWindow == NULL )
        {
            printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() );
            success = false;
        }
        else
        {
            //Get window surface
            gScreenSurface = SDL_GetWindowSurface( gWindow );
        }
    }

    return success;
}

string get(list<string> _list, int _i){
    list<string>::iterator it = _list.begin();
    for(int i=0; i<_i; i++){
        ++it;
    }
    return *it;
}

bool loadMedia()
{
    //Loading success flag
    bool success = true;
    for( int i = 0; i <= KEY_PRESS_SURFACE_TOTAL; ++i )
    {
        if (i != KEY_PRESS_SURFACE_TOTAL){
            Entity[ PLAYER_SURFACE ] = loadSurface( get(EntityPaths, i) );
        }
    }
    return success;
} 
void close()
{
    //Deallocate surfaces
    for( int i = 0; i < KEY_PRESS_SURFACE_TOTAL; ++i )
    {
        SDL_FreeSurface( Entity[ i ] );
        Entity[ i ] = NULL;
    }

    //Destroy window
    SDL_DestroyWindow( gWindow );
    gWindow = NULL;

    //Quit SDL subsystems
    SDL_Quit();
}

SDL_Surface* loadSurface( std::string path )
{
    //Load image at specified path
    SDL_Surface* loadedSurface = SDL_LoadBMP( path.c_str() );
    if( loadedSurface == NULL )
    {
        printf( "Unable to load image %s! SDL Error: %s\n", path.c_str(), SDL_GetError() );
    }

    return loadedSurface;
}


int main( int argc, char* args[] )
{
    //Start up SDL and create window
    if( !init() )
    {
        printf( "Failed to initialize!\n" );
    }
    else
    {
        //Load media
        if( !loadMedia() )
        {
            printf( "Failed to load media!\n" );
        }
        else
        {   
            //Main loop flag
            bool quit = false;

            //Event handler
            SDL_Event e;

            //Set default current surface
            gCurrentSurface = Entity[ PLAYER_SURFACE ];

            //While application is running
            while( !quit )
            {
                //Handle events on queue
                while( SDL_PollEvent( &e ) != 0 )
                {
                    //User requests quit
                    if( e.type == SDL_QUIT )
                    {
                        quit = true;
                    }
                    HandelKey(e);
                }
                SDL_Rect rect;
                rect.x = PlayerX;
                rect.y = PlayerY;
                rect.w = 0;
                rect.h = 0;
                //Apply the current image
                SDL_BlitSurface( gCurrentSurface, NULL, gScreenSurface, &rect );
            
                //Update the surface
                SDL_UpdateWindowSurface( gWindow );
            }
        }
    }

    //Free resources and close SDL
    close();

    return 0;
}

生成文件:

#OBJS specifies which files to compile as part of the project
OBJS = Main.cpp

#MODS specifies which Modules to Compile. 
#NOTE: only add .cpp files, no .h files!
MODS = Modules/HandleKeys.cpp

#CC specifies which compiler we're using
CC = g++

#COMPILER_FLAGS specifies the additional compilation options we're using
# -w suppresses all warnings
COMPILER_FLAGS = -w

#LINKER_FLAGS specifies the libraries we're linking against
LINKER_FLAGS = -lSDL2

#OBJ_NAME specifies the name of our exectuable
OBJ_NAME = Build/build.deb

#This is the target that compiles our executable
all : $(OBJS)
    $(CC) $(OBJS) $(MODS) $(COMPILER_FLAGS) $(LINKER_FLAGS) -o $(OBJ_NAME)

Global.h

#ifndef __GLOBAL_H__
#define __GLOBAL_H__
//Included by all files!
#include <SDL2/SDL.h>
#include <string>
#include <list>
using namespace std;

extern int SCREEN_WIDTH = 640;
extern int SCREEN_HEIGHT = 480;

extern char* title = "Top Down";
enum Entity
{
    PLAYER_SURFACE,
    KEY_PRESS_SURFACE_TOTAL
};

extern list<string> EntityPaths
{
    "Assets/Player.bmp",
};

//Starts up SDL and creates window
extern bool init();

//Loads media
extern bool loadMedia();

//Frees media and shuts down SDL
extern void close();

//Loads individual image
extern SDL_Surface* loadSurface( std::string path );

//The window we'll be rendering to
extern SDL_Window* gWindow;
    
//The surface contained by the window
extern SDL_Surface* gScreenSurface;

//The images that correspond to a keypress
extern SDL_Surface* Entity[ KEY_PRESS_SURFACE_TOTAL ];

//Current displayed image
extern SDL_Surface* gCurrentSurface;

extern int PlayerX;
extern int PlayerY;

extern const int PlayerSpeed;
#endif

HandleKeys.cpp:

#include "Global.h"
#include "HandleKeys.h"

void HandleKey(SDL_Event e){
    if( e.type == SDL_KEYDOWN )
    {
        //Select surfaces based on key press
        switch( e.key.keysym.sym )
        {
            case SDLK_UP:
            PlayerY -= PlayerSpeed;
            break;

            case SDLK_DOWN:
            PlayerY += PlayerSpeed;
            break;

            case SDLK_LEFT:
            PlayerX -= PlayerSpeed;
            break;

            case SDLK_RIGHT:
            PlayerX += PlayerSpeed;
            break;
        }
    }
}

手柄键h:

#ifndef __HANDLEKEYS_H__
#define __HANDLEKEYS_H__

extern void HandelKey(SDL_Event e);
#endif

生成文件输出:

g++ Main.cpp Modules/HandleKeys.cpp -w -lSDL2 -o Build/build.deb
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o:(.data+0x0): multiple definition of `SCREEN_WIDTH'; /tmp/ccdTvusg.o:(.data+0x0): first defined here
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o:(.data+0x4): multiple definition of `SCREEN_HEIGHT'; /tmp/ccdTvusg.o:(.data+0x4): first defined here
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o:(.data.rel.local+0x0): multiple definition of `title'; /tmp/ccdTvusg.o:(.data.rel.local+0x0): first defined here
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o:(.bss+0x0): multiple definition of `EntityPaths[abi:cxx11]'; /tmp/ccdTvusg.o:(.bss+0x0): first defined here
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o: warning: relocation against `PlayerSpeed' in read-only section `.text'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccdTvusg.o: in function `main':
Main.cpp:(.text+0x3e0): undefined reference to `HandelKey(SDL_Event)'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o: in function `HandleKey(SDL_Event)':
HandleKeys.cpp:(.text+0x49): undefined reference to `PlayerSpeed'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: HandleKeys.cpp:(.text+0x5f): undefined reference to `PlayerSpeed'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: HandleKeys.cpp:(.text+0x75): undefined reference to `PlayerSpeed'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: HandleKeys.cpp:(.text+0x8b): undefined reference to `PlayerSpeed'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status
make: *** [Makefile:23: all] Error 1

我在使用"extern"参数方面做了很多工作,但是没有任何中继工作,我还在global. h和main.cpp中定义了一些东西。
我使用的是pop操作系统(一个基于Ubuntu的Linux发行版)。
编辑:我添加了错误的main.cpp(我在vscode工作区中打开了另一个!)
Edit2:现在出现了一个新错误:

/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccwvE6bo.o: in function `main':
Main.cpp:(.text+0x3e0): undefined reference to `HandelKey(SDL_Event)'
collect2: error: ld returned 1 exit status
make: *** [Makefile:23: all] Error 1
vmdwslir

vmdwslir1#

台词

const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

Main.cpp和行中

extern int SCREEN_WIDTH = 640;
extern int SCREEN_HEIGHT = 480;

Global.h中是定义。带有初始化式的声明总是定义。
如果在多个翻译单元(即.cpp文件)中定义同一个变量,则违反了one-definition rule
我建议你改变

extern int SCREEN_WIDTH = 640;
extern int SCREEN_HEIGHT = 480;

Global.h中修改为:

extern const int SCREEN_WIDTH;
extern const int SCREEN_HEIGHT;

这样,它就只是一个声明,而不再是定义。
Global.h中定义的title也有类似的问题:

extern char* title = "Top Down";

这条线是一个定义。
头文件通常应该只包含变量的声明,而不是定义,因为否则,如果头文件包含在多个翻译单元中,将违反单定义规则。
因此,我建议您将此行更改为声明:

extern const char* title;

在其中一个翻译单元中,例如Main.cpp,您应该提供定义:

const char* title = "Top Down";

变量EntityPaths也有类似的问题:

extern list<string> EntityPaths
{
    "Assets/Player.bmp",
};

这是一个定义。您不应该在头文件中定义它。相反,您应该只提供一个声明:

extern list<string> EntityPaths;

您应该在一个翻译单元中定义它,例如在Main.cpp中。
Global.h中,您已经声明了PlayerSpeed

extern const int PlayerSpeed;

但是,您没有在任何地方定义它。我建议您添加

const int PlayerSpeed;

Main.cpp
这条线

extern void HandelKey(SDL_Event e);

HandleKeys.h中有一个打字错误。它应该是HandleKey,而不是HandelKey

相关问题