基类指针的向量,用于从派生类访问成员函数?正确的方法是什么?



我想开发一种棋盘游戏,目前使用带有基类指针的 std :: 向量管理各个字段(请参阅下面的源代码)。 基类仅描述一个没有任何特殊功能的字段。我使用派生类来"建模"各个类型的字段。

但是,在程序的后面(例如在游戏控制器端),我不知道如何访问特殊字段的成员函数。我认为以下源代码说明了我的担忧。

在这里,我想根据字段类型"动态"触发一些操作。

我真的很感激任何好的建议,因为我认为这是一件基本的事情。

PS:当然,如果能够完全省略各个类/特殊字段的类型信息,那就更优雅了。如果C++以某种方式知道它是哪个领域/类,那就太好了。

谢谢!

#include <cstdint>
#include <vector>
#include <iostream>
class cPlayer
{
public:
std::string name;
};
// base class for all fields of the board (common parts)
// used also for special fields like GO, FREE PARKING, GO TO JAIL
class cField 
{
public:
enum eType
{
STREET,
RAIL,
PRISON,
CARD,
SPECIAL
};
uint8_t     pos;
std::string name{};
eType type;   

// strasse
cField(const uint8_t& _pos, const std::string& _name, eType _type) :
pos(pos),
name(name),
type(_type)
{
} 
// strasse
cField(const uint8_t& _pos, const std::string& _name) :
pos(pos),
name(name)
{
} 
};
// derived class for all street fields of the baord
class cStreet : cField
{
public:
uint8_t     amountHouses;
uint16_t    propertyPrice;
cPlayer*    owner;
cStreet(const uint8_t& _pos, const std::string& _name, uint16_t _propertyPrice) :
cField(_pos, _name),
propertyPrice(_propertyPrice)
{
type = cField::STREET;
owner = nullptr;
}
std::string getStreetname(void)
{
return name;
}
};
class cRailstation : cField
{
public:
cPlayer*    owner;
uint16_t    Price;
cRailstation(const uint8_t& _pos, const std::string& _name, uint16_t _Price) :
cField(_pos, _name),
Price(_Price)
{
type = cField::RAIL;
owner = nullptr;
} 
std::string getStationname(void)
{
return name;
} 
};
class cPrison : cField
{
public:
std::vector<cPlayer*> visitors;
std::vector<cPlayer*> prisoners;
};
int main()
{

uint8_t pos = 0;                // current field position on board
std::string curFieldName{};     // current name of the field
// add some players
cPlayer player{"bob"};
cPlayer enemy1{"stuart"};
cPlayer enemy2{"kevin"};
std::vector<cField*> fields;
// build the board
fields.push_back( new cField{pos++, std::string("GO"), cField::SPECIAL});
fields.push_back( (cField*)new cStreet{pos++, std::string("Street 1"), 800} );
fields.push_back( (cField*)new cStreet{pos++, std::string("Street 2"), 800} );
fields.push_back( (cField*)new cRailstation{pos++, std::string("Railstation 1"), 300} );
fields.push_back( (cField*)new cStreet{pos++, std::string("Street 3"), 800} );
fields.push_back( (cField*)new cRailstation{pos++, std::string("Railstation 2"), 400} );
fields.push_back( (cField*)new cStreet{pos++, std::string("Street 4"), 800} );
fields.push_back( (cField*)new cStreet{pos++, std::string("Street 5"), 800} );
fields.push_back( (cField*)new cStreet{pos++, std::string("Prison"), 800} );
fields.push_back( (cField*)new cRailstation{pos++, std::string("Railstation 3"), 500} );
// ...    
// iterate over all fields on board
for(auto field : fields)    
{
// find element by name
if( field->type == cField::eType::SPECIAL) 
{
// here, we need only access to member function of the base class
}
else if( field->type == cField::eType::STREET)
{
// access to specific member functions of the derived class cStreet
// for example
//field->getOwner();
//field->buildHouse();
//field->getRent();
curFieldName = (cStreet*)field->getStreetname();
}
else if( field->type == cField::eType::RAIL)
{
// access to specific member functions of the derived class cRailstation
//field->getOwner();
curFieldName = (cRailstation*)field->getStationame();
}
else if( field->type == cField::eType::PRISON)
{
// access to specific member functions of the derived class cPrsion
//field->getPrisoners();
}
}
}

您正在寻找一种称为"多态性"的方法:

您有一个基类,它定义了一个泛型接口,该接口足以满足您对整个游戏机制的所有需求:

class Field // recommendation: drop Hungarian notation!
{
public:
virtual void action(Player& player) = 0;
// pure virtual in this case
// you might provide a default implementation instead
// but if meaningful is up to you to decide
};

然后在游戏运行中,您需要做的就是调用此函数:

// assuming std::vector:
player.position = (player.position + move) % fields.size();
fields[player.position].action();

– 完全不知道实际的字段类型。

现在,具体字段类型可以专门覆盖action函数:

class GotoJail
{
public:
void action(Player& player) override
{
// move player's piece to jail
}
};
class Street
{
Player* owner;
public:
void action(Player& player) override
{
if(owner)
{
// make player pay the street's fee to owner
}
else
{
// offer player to buy the street
}
}
};

Jail 就是一个很好的例子,这个界面可能还不够;你可能需要一个preMoveAction和一个postMoveAction,因为在监狱里,你可能不得不让玩家免费购买自己。

Field实例可能会向存储某些特定信息的Player实例(概念上类似于网络 cookie)提供特定Data类型的实例,例如,如果只是访问或实际被监禁,则用于监禁 - 甚至可能以 lambda 或函数指针的形式,轮到玩家时刚刚执行(也许替换preMoveAction以便您可以再次拥有单个action

最新更新