可以在没有主体的情况下调用声明的函数
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 链接,我很抱歉,显然在我获得更多 *** 信誉之前,我不能发布多个链接。
【讨论】:
以上是关于可以在没有主体的情况下调用声明的函数的主要内容,如果未能解决你的问题,请参考以下文章