可以在没有主体的情况下调用声明的函数

Posted

技术标签:

【中文标题】可以在没有主体的情况下调用声明的函数【英文标题】:can declared functions be called without a body 【发布时间】:2010-02-24 05:31:13 【问题描述】:

你能调用一个没有主体但在你的代码的头文件中声明的函数吗?请阅读下文了解我的情况。

我正在学习 c++,所以如果我的术语不正确,那么你知道为什么。无论如何,我正在阅读这本书,名为“高级 2D 游戏开发”,所以如果有人读过这本书,那么也许他们可以帮助我。在 c++ 中,他在 Advanced2D.h 中设置了 4 个外部函数

extern bool game_preload();
extern bool game_init(HWND);
extern void game_update();
extern void game_end();

后来,他在课堂上多次拜访他们,但从未给他们身体。他最终尝试将所有代码编译成 lib 文件,以便其他项目可以包含它并实际使用这 4 种方法。

他希望我转到解决方案属性/常规/输出目录并将其添加到发布和调试配置中

$(ProjectDir)..\lib\Advance2D.lib // It didn't work. Still added the libs at default location

只能在这样的另一个项目中使用上述方法。这是声明性方法获得它们的主体的时候。

#include <iostream>
#include "..\Engine\Advanced2D.h"
bool game_preload()

     //display engine version in a message box
     g_engine->message(g_engine->getVersionText(), "TEST ENGINE");
     //return fail to terminate the engine
     return false;


bool game_init(HWND hwnd)  return 0;
void game_update() 
void game_end() 

现在唯一的问题是我收到链接器错误

1>winmain.obj : error LNK2019: unresolved external symbol "bool __cdecl game_preload(void)" (?game_preload@@YA_NXZ) referenced in function _WinMain@16
1>c:\Engine\msvc8\Advance2D\Advance2D\..\lib\Advance2D.lib\Advance2D.exe : fatal error LNK1120: 1 unresolved externals

如果我不注释掉第一个项目中使用的那些方法,那么该项目永远不会被编译?

那家伙坚持说我在编译它时不应该收到任何链接器错误。我引用以下内容

假设您已将代码输入 指定的文件没有任何 错误,你应该能够 编译引擎项目。那里 应该没有依赖项 引擎,因为编译器假定 您将提供所需的库 在链接时(当您创建一个 使用引擎的 lib 可执行文件)。 这是一个相当复杂的问题 我们将在接下来再次检查 几个章节,因为我们加强 带有新模块的引擎和 功能。你不应该看到任何 链接器错误,只有编译器错误,如果 你输入错误 在代码中。

以下是高级二维头

// Advanced2D Engine
// Main header file
#ifndef _ADVANCED2D_H
#define _ADVANCED2D_H 1
#include <iostream>
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <dxerr.h>
#include "Timer.h"
#define VERSION_MAJOR 1
#define VERSION_MINOR 0
#define REVISION 0

#pragma comment(lib,"d3d9.lib")
#pragma comment(lib,"d3dx9.lib")
#pragma comment(lib, "winmm.lib")

//external variables and functions
extern bool gameover;
extern bool game_preload();
extern bool game_init(HWND);
extern void game_update();
extern void game_end();

namespace Advanced2D

    class Engine 
        private:
            int p_versionMajor, p_versionMinor, p_revision;
            HWND p_windowHandle;
            LPDIRECT3D9 p_d3d;
            LPDIRECT3DDEVICE9 p_device;
            LPDIRECT3DSURFACE9 p_backbuffer;
            LPD3DXSPRITE p_sprite_handler;
            std::string p_apptitle;
            bool p_fullscreen;
            int p_screenwidth;
            int p_screenheight;
            int p_colordepth;
            bool p_pauseMode;
            D3DCOLOR p_ambientColor;
            bool p_maximizeProcessor;
            Timer p_coreTimer;
            long p_frameCount_core;
            long p_frameRate_core;
            Timer p_realTimer;
            long p_frameCount_real;
            long p_frameRate_real;
        public:
            Engine();
            virtual ~Engine();
            int Init(int width, int height, int colordepth, bool fullscreen);
            void Close();
            void Update();
            void message(std::string message, std::string title = "ADVANCED 2D");
            void fatalerror(std::string message, std::string title = "FATAL ERROR");
            void Shutdown();
            void ClearScene(D3DCOLOR color);
            void SetDefaultMaterial();
            void SetAmbient(D3DCOLOR colorvalue);
            int RenderStart();
            int RenderStop();
            int Release();
            //accessor/mutator functions expose the private variables
            bool isPaused()  return this->p_pauseMode; 
            void setPaused(bool value)  this->p_pauseMode = value; 
            LPDIRECT3DDEVICE9 getDevice()  return this->p_device; 
            LPDIRECT3DSURFACE9 getBackBuffer()  return this->p_backbuffer; 
            LPD3DXSPRITE getSpriteHandler()  return this->p_sprite_handler; 
            void setWindowHandle(HWND hwnd)  this->p_windowHandle = hwnd; 
            HWND getWindowHandle()  return this->p_windowHandle; 
            std::string getAppTitle()  return this->p_apptitle; 
            void setAppTitle(std::string value)  this->p_apptitle = value; 
            int getVersionMajor()  return this->p_versionMajor; 
            int getVersionMinor()  return this->p_versionMinor; 
            int getRevision()  return this->p_revision; 
            std::string getVersionText();
            long getFrameRate_core()  return this->p_frameRate_core; ;
            long getFrameRate_real()  return this->p_frameRate_real; ;
            int getScreenWidth()  return this->p_screenwidth; 
            void setScreenWidth(int value)  this->p_screenwidth = value; 
            int getScreenHeight()  return this->p_screenheight; 
            void setScreenHeight(int value)  this->p_screenheight = value; 
            int getColorDepth()  return this->p_colordepth; 
            void setColorDepth(int value)  this->p_colordepth = value; 
            bool getFullscreen()  return this->p_fullscreen; 
            void setFullscreen(bool value)  this->p_fullscreen = value; 
            bool getMaximizeProcessor()  return this->p_maximizeProcessor; 
            void setMaximizeProcessor(bool value)  this->p_maximizeProcessor = value;
    ; //class
; //namespace
//define the global engine object (visible everywhere!)
extern Advanced2D::Engine *g_engine;
#endif

Advanced2d类

// Advanced2D Engine
// Main source code file
//includes
#include "Advanced2D.h"
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>
#include <list>
#include "winmain.h"

namespace Advanced2D

    Engine::Engine()
    
        srand((unsigned int)time(NULL));
        p_maximizeProcessor = false;
        p_frameCount_core = 0;
        p_frameRate_core = 0;
        p_frameCount_real = 0;
        p_frameRate_real = 0;
        p_ambientColor = D3DCOLOR_RGBA(255,255,255, 0);
        p_windowHandle = 0;
        p_pauseMode = false;
        p_versionMajor = VERSION_MAJOR;
        p_versionMinor = VERSION_MINOR;
        p_revision = REVISION;
        //set default values
        this->setAppTitle("Advanced2D");
        this->setScreenWidth(640);
        this->setScreenHeight(480);
        this->setColorDepth(32);
        this->setFullscreen(false);
        //window handle must be set later on for DirectX!
        this->setWindowHandle(0);
    

    Engine::~Engine()
    
        if (this->p_device) this->p_device->Release();
        if (this->p_d3d) this->p_d3d->Release();
    

    std::string Engine::getVersionText()
    
        std::ostringstream s;
        s << "Advanced2D Engine v" << p_versionMajor << "." << p_versionMinor
        << "." << p_revision;
        return s.str();
    

    void Engine::message(std::string message, std::string title)
    
        MessageBox(0, message.c_str(), title.c_str(), 0);
    

    void Engine::fatalerror(std::string message, std::string title)
    
        this->message(message,title);
        Shutdown();
    

    int Engine::Init(int width, int height, int colordepth, bool fullscreen)
    
        //initialize Direct3D

        this->p_d3d = Direct3DCreate9(D3D_SDK_VERSION);

        if (this->p_d3d == NULL) 
            return 0;
        

        //get system desktop color depth
        D3DDISPLAYMODE dm;
        this->p_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dm);

        //set configuration options for Direct3D
        D3DPRESENT_PARAMETERS d3dpp;
        ZeroMemory(&d3dpp, sizeof(d3dpp));
        d3dpp.Windowed = (!fullscreen);
        d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
        d3dpp.EnableAutoDepthStencil = TRUE;
        d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
        d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
        d3dpp.BackBufferFormat = dm.Format;
        d3dpp.BackBufferCount = 1;
        d3dpp.BackBufferWidth = width;
        d3dpp.BackBufferHeight = height;
        d3dpp.hDeviceWindow = p_windowHandle;

        //create Direct3D device
        this->p_d3d->CreateDevice(
        D3DADAPTER_DEFAULT,
        D3DDEVTYPE_HAL,
        this->p_windowHandle,
        D3DCREATE_HARDWARE_VERTEXPROCESSING,
        &d3dpp,
        &this->p_device);
        if (this->p_device == NULL) return 0;

        //clear the backbuffer to black
        this->ClearScene(D3DCOLOR_XRGB(0,0,0));

        //create pointer to the back buffer
        this->p_device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &this->p_backbuffer);

        //use ambient lighting and z-buffering
        this->p_device->SetRenderState(D3DRS_ZENABLE, TRUE);
        this->p_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
        this->SetAmbient(this->p_ambientColor);

        //initialize 2D renderer
        HRESULT result = D3DXCreateSprite(this->p_device, &this->p_sprite_handler);
        if (result != D3D_OK) return 0;
        //call game initialization extern function
        //if (!game_init(this->getWindowHandle())) return 0;
        //set a default material
        SetDefaultMaterial();
        return 1;
    


    void Engine::SetDefaultMaterial()
    
        D3DMATERIAL9 mat;
        memset(&mat, 0, sizeof(mat));
        mat.Diffuse.r = 1.0f;
        mat.Diffuse.g = 1.0f;
        mat.Diffuse.b = 1.0f;
        mat.Diffuse.a = 1.0f;
        p_device->SetMaterial(&mat);
    

    void Engine::ClearScene(D3DCOLOR color)
    
        this->p_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
        color, 1.0f, 0);
    

    void Engine::SetAmbient(D3DCOLOR colorvalue)
    
        this->p_ambientColor = colorvalue;
        this->p_device->SetRenderState(D3DRS_AMBIENT, this->p_ambientColor);
    
    int Engine::RenderStart()
    
        if (!this->p_device) return 0;
        if (this->p_device->BeginScene() != D3D_OK) return 0;
        return 1;
    
    int Engine::RenderStop()
    
        if (!this->p_device) return 0;
        if (this->p_device->EndScene() != D3D_OK) return 0;
        if (p_device->Present(NULL, NULL, NULL, NULL) != D3D_OK) return 0;
        return 1;
    

    void Engine::Shutdown()
    
        gameover = true;
    

    void Engine::Update()
    
        static Timer timedUpdate;
        //calculate core framerate
        p_frameCount_core++;
        if (p_coreTimer.stopwatch(999)) 
        p_frameRate_core = p_frameCount_core;
        p_frameCount_core = 0;
        
        //fast update with no timing
        game_update();

        //update with 60fps timing
        if (!timedUpdate.stopwatch(14)) 
            if (!this->getMaximizeProcessor())
            
                Sleep(1);
            
        
        else 
            //calculate real framerate
            p_frameCount_real++;

            if (p_realTimer.stopwatch(999)) 
                p_frameRate_real = p_frameCount_real;
                p_frameCount_real = 0;
            
            //begin rendering
            this->RenderStart();
            //done rendering
            this->RenderStop();
        
    
    void Engine::Close()
    
        game_end();
    
 //namespace

这里是 WinMain

#include <sstream>
#include "winmain.h"
#include "Advanced2D.h"
//macro to read the key states
#define KEY_DOWN(vk) ((GetAsyncKeyState(vk) & 0x8000)?1:0)
HINSTANCE g_hInstance;
HWND g_hWnd;
int g_nCmdShow;
//declare global engine object
Advanced2D::Engine *g_engine;
bool gameover;


//window event callback function
LRESULT WINAPI WinProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )

    switch( msg )
    
    case WM_QUIT:
    case WM_CLOSE:
    case WM_DESTROY:
    gameover = true;
    break;
    
    return DefWindowProc( hWnd, msg, wParam, lParam );

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int
nCmdShow)

    MSG msg;
    srand((unsigned int)time(NULL));
    g_hInstance = hInstance;
    g_nCmdShow = nCmdShow;
    DWORD dwStyle, dwExStyle;
    RECT windowRect;
    /**
    * Create engine object first!
    **/
    g_engine = new Advanced2D::Engine();
    //let main program have a crack at things before window is created
    if (!game_preload()) 
        MessageBox(g_hWnd, "Error in game preload!", "Error", MB_OK);
        return 0;
    
    //get window caption string from engine
    char title[255];
    sprintf_s(title, "%s", g_engine->getAppTitle().c_str());
    //set window dimensions
    windowRect.left = (long)0;
    windowRect.right = (long)g_engine->getScreenWidth();
    windowRect.top = (long)0;
    windowRect.bottom = (long)g_engine->getScreenHeight();
    //create the window class structure
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    //fill the struct with info
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)WinProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = NULL;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName = NULL;
    wc.lpszClassName = title;
    wc.hIconSm = NULL;
    //set up the window with the class info
    RegisterClassEx(&wc);
    //set up the screen in windowed or fullscreen mode?
    if (g_engine->getFullscreen())
    
        DEVMODE dm;
        memset(&dm, 0, sizeof(dm));
        dm.dmSize = sizeof(dm);
        dm.dmPelsWidth = g_engine->getScreenWidth();
        dm.dmPelsHeight = g_engine->getScreenHeight();
        dm.dmBitsPerPel = g_engine->getColorDepth();
        dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
        if (ChangeDisplaySettings(&dm, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) 
        MessageBox(NULL, "Display mode failed", NULL, MB_OK);
        g_engine->setFullscreen(false);
        
        dwStyle = WS_POPUP;
        dwExStyle = WS_EX_APPWINDOW;
        ShowCursor(FALSE);
        
        else 
        dwStyle = WS_OVERLAPPEDWINDOW;
        dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
        
        //adjust window to true requested size
        AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);
        //create the program window
        g_hWnd = CreateWindowEx( 0,
        title, //window class
        title, //title bar
        dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
        0, 0, //x,y coordinate
        windowRect.right - windowRect.left, //width of the window
        windowRect.bottom - windowRect.top, //height of the window
        0, //parent window
        0, //menu
        g_hInstance, //application instance
        0); //window parameters
        //was there an error creating the window?
        if (!g_hWnd) 
        MessageBox(g_hWnd, "Error creating program window!", "Error", MB_OK);
        return 0;
    
    //display the window
    ShowWindow(g_hWnd, g_nCmdShow);
    UpdateWindow(g_hWnd);
    //initialize the engine
    g_engine->setWindowHandle(g_hWnd);
    if (!g_engine->Init(g_engine->getScreenWidth(), g_engine->getScreenHeight(),
    g_engine->getColorDepth(), g_engine->getFullscreen())) 
    MessageBox(g_hWnd, "Error initializing the engine", "Error", MB_OK);
    return 0;
    
    // main message loop
    gameover = false;
    while (!gameover)
    
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        
        g_engine->Update();
    

    if (g_engine->getFullscreen()) 
        ShowCursor(TRUE);
    
    g_engine->Close();
    delete g_engine;
    return 1;

【问题讨论】:

关于术语:您对“声明”的使用是正确的。您缺少的术语是“定义”。定义一个函数或方法就是提供一个实现(花括号中的主体)。 【参考方案1】:

extern 关键字指定该函数将在您所在的编译对象外部的编译对象中实现 - 通常通过 lib 或其他对象。如果您要在一个 cpp 文件中(在任何函数体之外)声明一个全局范围内的变量,您只能在另一个 cpp 文件中使用外部 变量声明 来引用它 - 这样编译器就知道不要期望定义函数/变量但知道它存在。

这里发生的事情是,您要链接的对象实际上并没有被链接——它应该是。上面的答案告诉你如何 - 我不够快!从您的问题来看,您似乎拥有第二个项目的源代码,并且可能需要自己构建它 - 我当然不知道,但如果 .lib 不存在,那么您将需要这样做。

【讨论】:

看看Advanced2d类。如果你往底部看,你会看到 game_update();和游戏结束();它们是在 advanced2d.h 中声明的 2 个外部方法。他们被召唤,但他们没有尸体。因此,为了使 lib 文件存在,我必须按原样编译它,这就是我收到错误的时候。我可以将其注释掉,然后它会起作用。但是我会包含一个缺少这些代码行的库。 我明白 extern 关键字是什么。我不明白为什么当亚瑟坚持它应该编译时它不会编译。我已将问题缩小到这几行。游戏结束();和游戏更新(); 2 个在没有主体的情况下被调用的函数,也是导致整个应用程序抛出错误的唯一原因。【参考方案2】:

根据您的描述,我想您使用的是某个版本的 MS Visual C++。确保 ProjectProperties -> Linker-> Input -> Additional Dependencies 字段中有 Advance2D.lib。还要在 ProjectProperties -> Linker-> General -> Additional Library Directories 中添加此库的路径。不需要更改引擎项目的默认输出路径。顺便说一句,如果您是 C++ 初学者,最好先阅读 The C++ Programming Language 或 Thinking In C++ 之类的内容,然后再深入了解“高级”内容。

【讨论】:

问题是我无法编译项目而不注释掉 Advanced2D 类中的外部函数。因为该类正在调用这些函数,但它们的主体从未被制造出来。因此我收到了错误。 看我对九指的回复。【参考方案3】:

您的错误是链接器错误,而不是编译器错误。您的代码编译得很好,但链接器无法生成可执行文件,直到所有引用的函数都有一个主体。链接器要么不知道包含函数体的库,要么该库不包含它们。

在第一种情况下,您需要更改项目设置并指定正确的名称和位置,Vijay 已经描述过。

在第二种情况下,您应该检查该库的导出符号。如果库被编译为 C++,则函数的名称可能已被破坏以确保类型安全(需要在库中指定为导出以防止这种情况发生),或者它们可能具有不同的签名并且您必须更改您的 extern 语句。 VC有一个显示库信息的工具,我想是叫dumpbin。

【讨论】:

这就是为什么我认为写这本书的人没有正确编辑。因为他要我编译一些代码,只是变成一个lib文件。但问题是我无法编译代码,因为缺少外部方法的定义。他希望其他项目包含这些 lib 文件,然后定义方法。但是,如果我遇到错误,我就无法达到这一点【参考方案4】:

只是想补充一下,以防您仍然遇到此问题,或者如果其他人发现此线程并希望在阅读高级 2D 游戏开发手册时进行一些额外的故障排除。

在第 16 页,他将检查引擎的 VS 设置,确保您不仅将目标扩展更改为 .lib,还确保将配置类型设置为静态库 (.lib),而不是动态库 (.dll)。

如果您将配置类型保留为动态库,您将看到外部函数的 LNK2019 错误,因为 VC++ 链接器希望这些函数具有定义,以便生成工作的 DLL 文件。当你编译一个静态库时,外部变量、函数等的实际定义不需要出现,直到你最终将所有东西链接到一个 DLL 或 EXE 中(正如本书后续项目中所做的那样)。

一些可能对这个主题有帮助的链接:

使用 extern 指定链接:msdn.microsoft.com/en-us + /library/0603949d.aspx

演练:创建和使用静态库:msdn.microsoft.com/en-us + /library/ms235627%28VS.80%29.aspx

Microsoft Visual C++ 静态和动态库:http://www.codeproject.com/KB/cpp/libraries1.aspx

希望这些信息对您有用!

*注意:对于上面疯狂的 msdn 链接,我很抱歉,显然在我获得更多 *** 信誉之前,我不能发布多个链接。

【讨论】:

以上是关于可以在没有主体的情况下调用声明的函数的主要内容,如果未能解决你的问题,请参考以下文章

在没有显式命令的情况下在函数调用之间清除数组指针数据?

在 Python 中的文件末尾声明函数

函数定义函数声明函数调用以及extern跨文件的变量引用

[类和对象]构造和析构

ORACLE存储过程里可以声明过程和函数吗

可以在不调用 memset 的情况下从构造函数初始化器列表中将成员结构设为零吗?