C++ 传递一个 class 的列表和一个 subclass 的元素
C++ passing a list of a class with elements of a subclass
我是 C++ 的新手,我正在尝试制作一个简单的游戏。这是我的问题:
我创建了 class 名为 sprite:
class Sprite
{
private:
Point2D sp_pos;
Point2D sp_vel;
SDL_Surface* sp_img;
Point2D sp_center;
Point2D sp_size;
double sp_radius;
bool sp_animated;
int sp_frames;
int sp_cur_frame;
public:
Sprite() {}
Sprite(Point2D pos, Point2D vel, SDL_Surface *img, ImageInfo info, bool animated = false, int frames = 0);
virtual void draw(SDL_Surface* screen);
virtual void update();
void setInfo (ImageInfo info);
void setPos( Point2D pos ) { sp_pos = pos; }
void setVel( Point2D vel ) { sp_vel = vel; }
void setImg (SDL_Surface* img) { sp_img = img; }
void setNextFrame() { sp_cur_frame++; }
void setFrame( int frame ) { sp_cur_frame = frame; }
void setAnimated(bool animated) { sp_animated = animated; }
void changeVelX (int c) { sp_vel.setX(c);}
void changeVelY (int c) { sp_vel.setY(c);}
void changePosX (int c) { sp_pos.setX(c);}
void changePosY (int c) { sp_pos.setY(c);}
SDL_Surface* getImg() { return sp_img; }
Point2D getPos() { return sp_pos; }
Point2D getVel() { return sp_vel; }
Point2D getCenter() { return sp_center; }
Point2D getSize() { return sp_size; }
double getRadius() { return sp_radius; }
int getCurFrame() { return sp_cur_frame; }
int getFrames() { return sp_frames; }
bool collide(Sprite &another_sprite);
};
其中有一个方法叫做"collide",这个方法检测两个精灵之间的碰撞,其工作原理如下:
bool Sprite::collide(Sprite &another_sprite)
{
double d = getPos().dist(another_sprite.getPos());
if ( d < ( getRadius() + another_sprite.getRadius() ) )
return true;
else
return false;
}
该方法工作正常。我的问题出现在以下方面,我已经实现了不同的 classes,它们是 "Sprite" 的子classes,并且将在我的游戏中代表敌人,所以,例如我会有对象:Class enemy1 : public Sprite, Class enemy2 : public Sprite 等。它们是不同的,因为它们有不同的行为。我实现了另外两个名为 group_collide 和 group_group_collide 的辅助函数,其工作方式如下:
bool group_collide(std::list<Sprite> &group, Sprite other_object)
{
bool collision = false;
for (std::list<Sprite>::iterator sprite = group.begin(), end = group.end(); sprite != end; ++sprite)
{
if (sprite->collide(other_object))
{
Sprite exp = Sprite(sprite->getPos(), Point2D(0, 0), exp_image, exp_info, true, 7);
exp_group.push_back(exp);
if( Mix_PlayChannel( -1, explosion, 0 ) == -1 )
{
//abort();
}
sprite = group.erase(sprite);
collision = true;
}
}
return collision;
}
int group_group_collide(std::list<Sprite> &group, std::list<Sprite> &other_group)
{
int scored = 0;
for (std::list<Sprite>::iterator it1 = group.begin(), end1 = group.end(); it1 != end1; ++it1)
{
if (group_collide(other_group, *it1))
{
it1 = group.erase(it1);
scored += 10;
}
}
return scored;
}
所以,实际上,group collide 将检测精灵和精灵列表之间的碰撞,而 group_group_collide 将检测精灵组(两个不同的列表)之间的碰撞。出现的问题是:至少会有 4 种类型的敌人,它们都是我的 Sprite class 的子class,但是当我创建一个 sprite 列表时出现编译错误并且添加子 class 精灵的元素。我的解决方案是为所有类型的敌人编写一个 group_collide 和 group_group 碰撞的方法,但这很不优雅。有没有更好的方法来解决这个问题?
编辑:
感谢您的建议。我按照您的建议将列表定义为指针列表:
std::list<Sprite*> enemy_group;
例如,我添加了 class "Kamikaze" 的元素,它是 sprite 的子 class,以这种方式(方法更新在这个 class):
enemy_group.push_back(new Kamikaze(enemy_pos, enemy_vel, 0, enemy_image, enemy_info));
但是,在遍历列表时:
for (list<Sprite*>::iterator it = enemy_group.begin(), end = enemy_group.end(); it != end; ++it) {
(*it)->draw(screen);
(*it)->update();
if ((*it)->getPos().getY() > SCREEN_HEIGHT + 30)
{
delete *it;
it = enemy_group.erase(it);
}
}
方法更新是从 Sprite class 调用的,而不是 Kamikaze class,因此我也遇到了对象切片问题,也许我所做的有问题?
I get compilation errors when I create a list of sprites and add elements that are subclasses of sprites.
所有派生的 类 都将是 'sliced',以便适合像 std::list 这样的对象容器。 std::list<Sprite*> //(Preferably a smart pointer)
将避免此问题,但实际对象必须存储在其他地方。
正如 Laserbreath 在回答中给出的那样,您应该将 std::list<Sprite>
替换为 std::list<Sprite*>
。目前,任何通过的 subclass 都将缩减为 Sprite。使用指针列表可以避免这种情况。
您应该使用虚函数来获得想要的结果。
在基地 class:
//This way you'll have a default collision method for a Sprite...
//...but you'll be able to re-define it in subclasses.
//I don't do THIS ONE often so I'm not sure if you'll have to perform additional steps
virtual bool Sprite::collide(Sprite &another_sprite)
{
...
}
//And this way you are making collied a pure virtual function and Sprite an abstract class...
//...meaning you can't instantiate it, but you can use it with a pointer or reference.
//Every subclass of Sprite must have a body of function collide defined
//you do this only in header of class Sprite
virtual bool virtual bool Sprite::collide(Sprite &another_sprite) = 0;
中分class
//if you have made collide pure virtual function, you can do this
//not sure about the first case though
bool SubSprite::collide(Sprite &another_sprite)
{
...
}
因此,当从 Sprite pointer 或 reference 调用 collide
方法时,调用将是 重定向到子class.
的指定函数
group_collide也是如此。
补充说明:
//lets assumme you have 2 sprite types, Kamikaze and NotKamikaze
Sprite *sprite1 = new Kamikaze();
Sprite *sprite2 = new NotKamikaze();
//and lets assume collide uses pointer instead, just for this example, so it's shorter
virtual bool Sprite::collide(Sprite *another_sprite) = 0;
bool Kamikaze::collide(Sprite *another_sprite);
bool NotKamikaze::collide(Sprite *another_sprite);
//when you call collide from Sprite*, it **automatically** picks the sub-class collude method
//sprite1 is of type Sprite*, but allocated memory is of Kamikaze
//so the only function called is Kamikaze::collide
sprite1->collide(sprite2);
//sprite2 is of type Sprite*, but allocated memory is of NotKamikaze
//so the only function called is NotKamikaze::collide
sprite2->collide(sprite2);
因此,每当从指针或引用调用纯虚函数 collide
时,程序 自动 选择正确的。如果您希望每个精灵类型都不同,那么这就是您所需要的。
这就是 "object slicing" 成为问题的地方。 Sprite 可以作为指针或引用存在,但只能指向非抽象子class。所以 Sprite *Sprite sprite1 = new Sprite();
不起作用,但 Sprite *Sprite sprite1 = new SpriteSubClass();
起作用。
如果你让 Sprite::collide
不是纯虚拟的,Sprite 将不再是抽象的 class,你 可以 使方法 运行 来自 Sprite class 本身,你可以做到 Sprite *Sprite sprite1 = new Sprite();
。但我非常怀疑你是否需要它,因为你只看 subclasses.
每当共享一个方法名称,但其行为因子class而不同时,它必须是虚拟的。
我是 C++ 的新手,我正在尝试制作一个简单的游戏。这是我的问题:
我创建了 class 名为 sprite:
class Sprite
{
private:
Point2D sp_pos;
Point2D sp_vel;
SDL_Surface* sp_img;
Point2D sp_center;
Point2D sp_size;
double sp_radius;
bool sp_animated;
int sp_frames;
int sp_cur_frame;
public:
Sprite() {}
Sprite(Point2D pos, Point2D vel, SDL_Surface *img, ImageInfo info, bool animated = false, int frames = 0);
virtual void draw(SDL_Surface* screen);
virtual void update();
void setInfo (ImageInfo info);
void setPos( Point2D pos ) { sp_pos = pos; }
void setVel( Point2D vel ) { sp_vel = vel; }
void setImg (SDL_Surface* img) { sp_img = img; }
void setNextFrame() { sp_cur_frame++; }
void setFrame( int frame ) { sp_cur_frame = frame; }
void setAnimated(bool animated) { sp_animated = animated; }
void changeVelX (int c) { sp_vel.setX(c);}
void changeVelY (int c) { sp_vel.setY(c);}
void changePosX (int c) { sp_pos.setX(c);}
void changePosY (int c) { sp_pos.setY(c);}
SDL_Surface* getImg() { return sp_img; }
Point2D getPos() { return sp_pos; }
Point2D getVel() { return sp_vel; }
Point2D getCenter() { return sp_center; }
Point2D getSize() { return sp_size; }
double getRadius() { return sp_radius; }
int getCurFrame() { return sp_cur_frame; }
int getFrames() { return sp_frames; }
bool collide(Sprite &another_sprite);
};
其中有一个方法叫做"collide",这个方法检测两个精灵之间的碰撞,其工作原理如下:
bool Sprite::collide(Sprite &another_sprite)
{
double d = getPos().dist(another_sprite.getPos());
if ( d < ( getRadius() + another_sprite.getRadius() ) )
return true;
else
return false;
}
该方法工作正常。我的问题出现在以下方面,我已经实现了不同的 classes,它们是 "Sprite" 的子classes,并且将在我的游戏中代表敌人,所以,例如我会有对象:Class enemy1 : public Sprite, Class enemy2 : public Sprite 等。它们是不同的,因为它们有不同的行为。我实现了另外两个名为 group_collide 和 group_group_collide 的辅助函数,其工作方式如下:
bool group_collide(std::list<Sprite> &group, Sprite other_object)
{
bool collision = false;
for (std::list<Sprite>::iterator sprite = group.begin(), end = group.end(); sprite != end; ++sprite)
{
if (sprite->collide(other_object))
{
Sprite exp = Sprite(sprite->getPos(), Point2D(0, 0), exp_image, exp_info, true, 7);
exp_group.push_back(exp);
if( Mix_PlayChannel( -1, explosion, 0 ) == -1 )
{
//abort();
}
sprite = group.erase(sprite);
collision = true;
}
}
return collision;
}
int group_group_collide(std::list<Sprite> &group, std::list<Sprite> &other_group)
{
int scored = 0;
for (std::list<Sprite>::iterator it1 = group.begin(), end1 = group.end(); it1 != end1; ++it1)
{
if (group_collide(other_group, *it1))
{
it1 = group.erase(it1);
scored += 10;
}
}
return scored;
}
所以,实际上,group collide 将检测精灵和精灵列表之间的碰撞,而 group_group_collide 将检测精灵组(两个不同的列表)之间的碰撞。出现的问题是:至少会有 4 种类型的敌人,它们都是我的 Sprite class 的子class,但是当我创建一个 sprite 列表时出现编译错误并且添加子 class 精灵的元素。我的解决方案是为所有类型的敌人编写一个 group_collide 和 group_group 碰撞的方法,但这很不优雅。有没有更好的方法来解决这个问题?
编辑:
感谢您的建议。我按照您的建议将列表定义为指针列表:
std::list<Sprite*> enemy_group;
例如,我添加了 class "Kamikaze" 的元素,它是 sprite 的子 class,以这种方式(方法更新在这个 class):
enemy_group.push_back(new Kamikaze(enemy_pos, enemy_vel, 0, enemy_image, enemy_info));
但是,在遍历列表时:
for (list<Sprite*>::iterator it = enemy_group.begin(), end = enemy_group.end(); it != end; ++it) {
(*it)->draw(screen);
(*it)->update();
if ((*it)->getPos().getY() > SCREEN_HEIGHT + 30)
{
delete *it;
it = enemy_group.erase(it);
}
}
方法更新是从 Sprite class 调用的,而不是 Kamikaze class,因此我也遇到了对象切片问题,也许我所做的有问题?
I get compilation errors when I create a list of sprites and add elements that are subclasses of sprites.
所有派生的 类 都将是 'sliced',以便适合像 std::list 这样的对象容器。 std::list<Sprite*> //(Preferably a smart pointer)
将避免此问题,但实际对象必须存储在其他地方。
正如 Laserbreath 在回答中给出的那样,您应该将
std::list<Sprite>
替换为std::list<Sprite*>
。目前,任何通过的 subclass 都将缩减为 Sprite。使用指针列表可以避免这种情况。您应该使用虚函数来获得想要的结果。
在基地 class:
//This way you'll have a default collision method for a Sprite...
//...but you'll be able to re-define it in subclasses.
//I don't do THIS ONE often so I'm not sure if you'll have to perform additional steps
virtual bool Sprite::collide(Sprite &another_sprite)
{
...
}
//And this way you are making collied a pure virtual function and Sprite an abstract class...
//...meaning you can't instantiate it, but you can use it with a pointer or reference.
//Every subclass of Sprite must have a body of function collide defined
//you do this only in header of class Sprite
virtual bool virtual bool Sprite::collide(Sprite &another_sprite) = 0;
中分class
//if you have made collide pure virtual function, you can do this
//not sure about the first case though
bool SubSprite::collide(Sprite &another_sprite)
{
...
}
因此,当从 Sprite pointer 或 reference 调用 collide
方法时,调用将是 重定向到子class.
group_collide也是如此。
补充说明:
//lets assumme you have 2 sprite types, Kamikaze and NotKamikaze
Sprite *sprite1 = new Kamikaze();
Sprite *sprite2 = new NotKamikaze();
//and lets assume collide uses pointer instead, just for this example, so it's shorter
virtual bool Sprite::collide(Sprite *another_sprite) = 0;
bool Kamikaze::collide(Sprite *another_sprite);
bool NotKamikaze::collide(Sprite *another_sprite);
//when you call collide from Sprite*, it **automatically** picks the sub-class collude method
//sprite1 is of type Sprite*, but allocated memory is of Kamikaze
//so the only function called is Kamikaze::collide
sprite1->collide(sprite2);
//sprite2 is of type Sprite*, but allocated memory is of NotKamikaze
//so the only function called is NotKamikaze::collide
sprite2->collide(sprite2);
因此,每当从指针或引用调用纯虚函数 collide
时,程序 自动 选择正确的。如果您希望每个精灵类型都不同,那么这就是您所需要的。
这就是 "object slicing" 成为问题的地方。 Sprite 可以作为指针或引用存在,但只能指向非抽象子class。所以 Sprite *Sprite sprite1 = new Sprite();
不起作用,但 Sprite *Sprite sprite1 = new SpriteSubClass();
起作用。
如果你让 Sprite::collide
不是纯虚拟的,Sprite 将不再是抽象的 class,你 可以 使方法 运行 来自 Sprite class 本身,你可以做到 Sprite *Sprite sprite1 = new Sprite();
。但我非常怀疑你是否需要它,因为你只看 subclasses.
每当共享一个方法名称,但其行为因子class而不同时,它必须是虚拟的。