vector<Spell*>递归调用cast()触发段错误问题求助
段错误问题:vector<Spell*>容器遍历调用cast()方法崩溃
我有一个vector<Spell*>类型的容器spells,想调用容器首个元素的cast()函数,依次调用容器内所有Spell*对象的cast()方法,但程序执行到me->cast(me, pos, 0.0f, capacity-1, draw);时触发段错误崩溃。
之前未使用指针容器时,代码虽能运行,但调用完最后一个Spell后会触发SIGSEGV错误。
期望输出(当draw=1时):
Empty Slot cast (virtual)
Empty Slot cast (virtual)
Empty Slot cast (virtual)
Spark Bolt cast
当draw=2时:
Empty Slot cast (virtual)
Empty Slot cast (virtual)
Empty Slot cast (virtual)
Spark Bolt cast
Empty Slot cast (virtual)
出错代码
#include <iostream> #include <vector> using namespace std; typedef struct Vector2 { float x; float y; } Vector2; class Spell { protected: Vector2 pos; string name; public: Spell() { pos = {1, 2}; name = "Empty Slot"; } virtual void cast(Spell *me, Vector2 from, float angle, int capacity, int draw) { cout << name << " cast (virtual)" << endl; if (draw > 0 && capacity > 0) { me++; me->cast(me, pos, 0.0f, capacity-1, draw); } } }; class SparkBolt : public Spell { public: SparkBolt () { pos = {0, 0}; name = "Spark Bolt"; } void cast(Spell *me, Vector2 from, float angle, int capacity, int draw) { cout << name << " cast" << endl; if (draw > 0 && capacity > 1) { me++; me->cast(me, pos, 0.0f, capacity-1, draw-1); } } }; class SpellStorage { private: int capacity; vector<Spell *> spells; public: explicit SpellStorage(int capacity) { SpellStorage::capacity = capacity; for (int i = 0; i < capacity; i++) { spells.emplace_back(new Spell()); } } void insertSpell(Spell *spell, int slot) { spells.at(slot-1) = spell; } void cast() { spells.at(0)->cast(spells.at(0), {3.0f, 4.0f}, 0.0f, capacity, 1); } }; //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ int main() { SpellStorage test = SpellStorage(5); test.insertSpell(new SparkBolt(), 4); test.cast(); return 0; }
错误原因
核心问题是错误地将单个Spell*指针当作数组指针操作:
spells.at(0)返回的是容器中第一个元素的指针,指向单个Spell对象,并非连续内存数组的起始地址。- 容器存储的是指针,这些指针本身的地址是连续的,但它们指向的
Spell对象内存地址是分散的。 - 执行
me++会让指针跳到当前对象内存之后的非法区域,后续调用me->cast()必然访问非法内存,触发段错误。
解决方案
方案1:让SpellStorage直接负责遍历(最简方案)
修改SpellStorage的cast()方法,直接遍历容器调用每个Spell的cast(),同时通过draw参数控制调用次数:
// 修改SpellStorage的cast方法 void cast(int draw = 1) { int invokedCount = 0; for (auto spell : spells) { if (invokedCount >= draw) break; spell->cast({3.0f, 4.0f}, 0.0f); invokedCount++; } } // 简化Spell和SparkBolt的cast方法,移除无用参数 class Spell { // ... 其他代码不变 virtual void cast(Vector2 from, float angle) { cout << name << " cast (virtual)" << endl; } }; class SparkBolt : public Spell { // ... 其他代码不变 void cast(Vector2 from, float angle) override { cout << name << " cast" << endl; } };
调用时直接使用test.cast(1)或test.cast(2)即可得到期望输出。
方案2:传递容器引用+索引实现递归遍历
如果需要保留递归逻辑,可将容器引用和当前索引传递给cast()方法,通过索引定位下一个元素:
// 修改Spell的cast方法 class Spell { // ... 其他代码不变 virtual void cast(vector<Spell*>& spells, int currentIndex, Vector2 from, float angle, int draw) { cout << name << " cast (virtual)" << endl; if (draw > 0 && currentIndex + 1 < spells.size()) { spells[currentIndex+1]->cast(spells, currentIndex+1, pos, 0.0f, draw - 1); } } }; // 修改SparkBolt的cast方法 class SparkBolt : public Spell { // ... 其他代码不变 void cast(vector<Spell*>& spells, int currentIndex, Vector2 from, float angle, int draw) override { cout << name << " cast" << endl; if (draw > 0 && currentIndex + 1 < spells.size()) { spells[currentIndex+1]->cast(spells, currentIndex+1, pos, 0.0f, draw - 1); } } }; // 修改SpellStorage的cast方法 class SpellStorage { // ... 其他代码不变 void cast(int draw = 1) { if (!spells.empty()) { spells[0]->cast(spells, 0, {3.0f, 4.0f}, 0.0f, draw); } } };
这种方式通过合法的容器索引访问下一个元素,避免了非法内存操作。
内容的提问来源于stack exchange,提问作者Bazza491
相关产品推荐
相关产品推荐

