我正在尝试创建一个模板化的通用菜单按钮类,因此我可以在任何类中创建此按钮。我想创建一个指向该类中不同函数的void函数指针,因此当您单击New Game按钮时,它将调用NewGame()函数等。
我对创建函数指针的想法还有点新意,并希望得到一些建议。
每当我尝试使用此Menubutton编译代码时,我都会收到一个疯狂的链接错误。
这是错误:
错误1错误LNK2019:未解决 外部符号“public:void __thiscall MENUBUTTON :: Draw(void)“ (?画出@?$ @菜单按钮@@@@ VTitleScreen QAEXXZ) 在函数“public中引用: 虚空__thiscall TitleScreen ::抽奖(无效)” (?Draw @ TitleScreen @@ UAEXXZ)TitleScreen.obj
MenuButton.h
template <class t>
struct MENUBUTTON
{
SPRITE Normal; // Sprite to display when not hovered over or pressed down
SPRITE Hover; // Sprite to display when hovered over
RECTANGLE HoverBounds; // The Rectangle that activates the hover flag
t* pClass; // Pointer to the templated class
void (t::*ClickFunction)(); // Pointer to void function
void SetButton(int xPos, int yPos, int width, int height, int hPadLeft, int hPadTop, int hWidth, int hHeight, LPCTSTR normalFilePath, LPCTSTR hoverFilePath, t* objectClass, void (t::*ClickFunction)());
bool IsMouseHover();
void CheckPressed();
void Draw();
};
MenuButton.cpp
#include "Global.h"
template <class t>
void MENUBUTTON<t>::SetButton(int xPos, int yPos, int width, int height, int hPadLeft, int hPadTop, int hWidth, int hHeight, LPCTSTR normalFilePath, LPCTSTR hoverFilePath, t* objectClass, void (t::*ClickFunction)())
{
// Position
Hover.position.x = Normal.position.x = xPos;
Hover.position.y = Normal.position.y = yPos;
Hover.position.z = Normal.position.z = 0;
// Width / Height
Hover.width = Normal.width = width;
Hover.height = Normal.height = height;
// Hover RECTANGLE
HoverBounds.x = xPos + hPadLeft;
HoverBounds.y = yPos + hPadTop;
HoverBounds.width = hWidth;
HoverBounds.height = hHeight;
// Load the Sprites
LoadSprite(&Normal, normalFilePath, width, height, 1, 1);
LoadSprite(&Hover, hoverFilePath, width, height, 1, 1);
// Set the Click function pointer
this->pClass = objectClass;
this->ClickFunction = ClickFunction;
}
template <class t>
void MENUBUTTON<t>::Draw()
{
if(IsMouseHover())
{
DrawSprite(&Hover, 0, Hover.position.x, Hover.position.y, Hover.position.z);
}
else
{
DrawSprite(&Normal, 0, Normal.position.x, Normal.position.y, Normal.position.z);
}
}
template <class t>
bool MENUBUTTON<t>::IsMouseHover()
{
return (((InputData.MousePosition.x >= HoverBounds.x) && (InputData.MousePosition.x <= (HoverBounds.x + HoverBounds.width))) &&
((InputData.MousePosition.y >= HoverBounds.y) && (InputData.MousePosition.y <= (HoverBounds.y + HoverBounds.height)))) ? true : false;
}
这是我使用菜单按钮的标题屏幕。
TitleScreen.h
class TitleScreen : public BaseState
{
// SPRITES
SPRITE titleScreenBG;
// MENU BUTTONS
MENUBUTTON<TitleScreen> playButton;
MENUBUTTON<TitleScreen> quitButton;
public:
TitleScreen();
virtual void Initialize();
virtual void End();
virtual void Update(float dt, INPUTDATA* input);
virtual void Draw();
void QuitGame();
void NewGame();
};
TitleScreen.cpp
#include "Global.h"
// Constructors
TitleScreen::TitleScreen()
{
}
// Virtual Voids
void TitleScreen::End()
{
}
void TitleScreen::Initialize()
{
this->Enabled = true;
this->Visible = true;
// Initialize sprites
ZeroMemory(&titleScreenBG, sizeof(SPRITE));
LoadSprite(&titleScreenBG, TEXT("../../PNG/TitleScreenBG.png"), 1440, 900, 1, 1);
titleScreenBG.position.x = titleScreenBG.position.y = titleScreenBG.position.z = 0;
// Initialize buttons
ZeroMemory(&playButton, sizeof(MENUBUTTON<TitleScreen>));
playButton.SetButton(55, 170, // x , y
512, 128, // width, height
10, 10, // Left, Top Padding
400, 70, // Hover width, Hover height
TEXT("../../PNG/NewGame.png"), TEXT("../../PNG/NewGameH.png"),
this, &TitleScreen::NewGame);
ZeroMemory(&quitButton, sizeof(MENUBUTTON<TitleScreen>));
quitButton.SetButton(55, 240, // x , y
512, 128, // width, height
10, 10, // Left, Top Padding
190, 70, // Hover width, Hover height
TEXT("../../PNG/QuitButton.png"), TEXT("../../PNG/QuitButtonH.png"),
this, &TitleScreen::QuitGame);
}
void TitleScreen::Update(float dt, INPUTDATA* input)
{
}
void TitleScreen::Draw()
{
StartRender();
DrawSprite(&titleScreenBG, 0, titleScreenBG.position.x, titleScreenBG.position.y, titleScreenBG.position.z);
playButton.Draw();
quitButton.Draw();
EndRender();
}
// Public Methods
void TitleScreen::QuitGame()
{
CloseDirect3D();
}
void TitleScreen::NewGame()
{
CloseDirect3D();
}
如果有人对如何以不同的方式使按钮动态显示任何情况有任何建议,或者知道这个问题是什么,请帮忙! :)
答案 0 :(得分:1)
要摆脱链接错误,请将template
文件中以.cpp
开头的所有方法定义移至.h
文件。在您的代码中,移动它们:
template <class t> void MENUBUTTON<t>::SetButton ... { ... }
template <class t> void MENUBUTTON<t>::Draw ... { ... }
它不能与.cpp
文件一起使用的原因是编译器将MENUBUTTON<Foo>
和MENUBUTTON<Bar>
等视为不同的类,并且每当它生成并编译这样的类时它被使用了。因此,如果您在编译MENUBUTTON<TitleScreen>
时使用titlescreen.cpp
,则MENUBUTTON<TitleScreen>
代码将被编译到目标文件titlescreen.o
中。但是方法SetButton
和Draw
在链接时将丢失,因为它们未在titlescreen.cpp
或其中包含的任何.h
文件中定义。 MenuButton.o
也不会包含它,因为MenuButton.cpp
不需要MENUBUTTON<TitleScreen>
。
答案 1 :(得分:0)
为了澄清第一个答案,你不能拥有一个在标题中声明的模板类,然后在一个单独的编译翻译单元(即.cpp文件)中实现。你必须将所有代码放在标题中。您可以将它全部放在初始类声明中,或者使用“inline”首先使用函数声明该类,然后在标题中进一步实现特定的函数。
答案 2 :(得分:0)
说到更好的方法......为什么你需要一个模板? 普通的旧虚函数或指向成员的指针都可以。 这将是Command Design Pattern的完美应用。 模板为您提供编译时多态性,而您可能正在寻找运行时多态性。