我一直在编写一个简单的棋盘游戏,以便在实践中学习C ++的概念。我已经实现了它:它由tile组成,每个tile都是一个继承自父类的子类。该板是一个具有瓦片矢量的类。
有几种瓷砖。其中一些可以由玩家购买。有几种不同类型的可购买的瓷砖以及不同的属性,所以我认为为可以购买的瓷砖制作基础类TileOnSale并制作实际类型的子类是可爱的,其中两个我在下面的代码中提供
现在我的问题是如何访问未在父类(TileOnSale)中定义的子成员函数? Board使用各种不同的tile进行初始化,因此我可以使用getTile(int location)函数从那里提取Tile。但是,这被解释为仅仅是Tile,而不是TileOnSale或StreetTile。我知道无法通过这种方式掌握StreetTile的buildHouses功能。
那么,是否有一种强大的,甚至更好的方法呢?我可以制作模板或其他东西来保存可能是StreetTiles或StationTiles的Tile对象或其他类似的Tile吗? 或者我应该重新设计类结构?
这是一个简单的代码。我试图只提供理解问题所需的内容。此外,最初的Tile和Board都在他们自己的头文件中。我决定没有必要向Player类显示拥有TileOnSale对象的向量,但是它保留了与Board完全相同的访问问题。
// Board.h
#include "Tile.h"
typedef vector<Tile> Tiles;
class Board
{
public:
Board();
~Board();
Tile getTile(int location);
private:
Tiles tiles;
};
// Tile.h
class Tile
{
public:
Tile();
~Tile();
protected:
tileType tile_type; // this is enum containing unique type
string description;
};
class TileOnSale : public Tile
{
public:
TileOnSale();
~TileOnSale();
virtual int getRent() const { return 0; };
};
class StreetTile : public TileOnSale
{
public:
StreetTile();
~StreetTile();
int getRent() override;
void buildHouses(int number);
private:
int houses;
};
class StationTile : public TileOnSale
{
public:
StationTile();
~StationTile();
int getRent() override;
};
编辑:为代码添加了可能澄清的评论。
答案 0 :(得分:2)
您可能需要查看visitor pattern。
从本质上讲,访问者可以在不修改类本身的情况下向一个类族添加新的虚函数;相反,我们创建了一个访问者类,它实现了虚函数的所有适当的特化。访问者将实例引用作为输入,并通过双重调度实现目标。
双重调度意味着您实际上正在调用虚拟函数两次:首先是主题,然后以多态方式调用访问者。
在您的情况下,只有一种方法,即建造房屋,但您可能希望稍后添加其他方法(例如在屏幕上绘制它们)。鉴于您当前的示例,您应该将此方法添加到Tile和StreetTile:
virtual void accept(Visitor& v) { v.visit(*this); }
这是访客基类实现:
class Visitor {
public:
virtual void accept(Tile& t) = 0;
virtual void accept(StreetTile& t) = 0;
};
之后,您可以实现Builder类:
class Builder: public Visitor {
private:
int numberOfHouses;
public:
Builder(int n): numberOfHouses(n) {}
virtual void accept(Tile& t) {}
virtual void accept(StreetTile& t) {
t.buildHouses(numberOfHouses);
}
};
之后你要做的就是构建这样一个构建器,并在你的tile矢量中的每个tile上调用它:
Builder b(10);
for (Tile tile : tiles) {
tile.accept(b);
}
答案 1 :(得分:1)
一种简单的方法是为每种类型添加唯一的id(枚举或字符串)。播放器类可以询问类型(在基类中定义)并相应地转换为派生类。
由于它需要在派生(例如专门)类上调用函数,因此它具有执行强制转换的知识。
具有类型ID也非常适合调试。