Home > other >  Vector of object pointers recursive function using next object pointer in vector
Vector of object pointers recursive function using next object pointer in vector

Time:11-13

I have a vector (vector<Spell *> spells;) and I want to be able to call the cast() function on the first element of the vector and have the spells cast the Spell* in the vector but the program will reach

me->cast(me, pos, 0.0f, capacity-1, draw);

and run into a segmentation fault, crashing the program.

My code:

#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;
}

Before I realised that the vector had to be a vector of Spell pointers for the Cast() polymorphism to work, the code worked fine but would return a sigsev fault after casting the last Spell in the vector.

I am expecting the program to print:

Empty Slot cast (virtual)
Empty Slot cast (virtual)
Empty Slot cast (virtual)
Spark Bolt cast

or

Empty Slot cast (virtual)
Empty Slot cast (virtual)
Empty Slot cast (virtual)
Spark Bolt cast
Empty Slot cast (virtual)

if draw = 2 (so the entire vector is cast)

CodePudding user response:

me increments the pointer but me isn't a pointer to an array so your code has undefined behaviour. Each pointer in your vector is unreleated to the rest and you can't use pointer arithmetic to traverse between them. You'd be better of using iterators instead:

#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(std::vector<Spell*>::iterator me, Vector2 from, float angle, int capacity, int draw) {
    if (draw > 0 && capacity > 0) {
      cout << name << " cast (virtual)" << endl;
      me  ;
      (*me)->cast(me, pos, 0.0f, capacity - 1, draw);
    }
  }
};

class SparkBolt : public Spell {
public:
  SparkBolt() {
    pos = { 0, 0 };
    name = "Spark Bolt";
  }
  void cast(std::vector<Spell*>::iterator me, Vector2 from, float angle, int capacity, int draw) {
    if (draw > 0 && capacity > 1) {
      cout << name << " cast" << endl;
      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.begin(), { 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;
}

You'll also need to check the capacity before recursing to ensure you don't dereference the past the end iterator:

      if (capacity > 1) {
        (*me)->cast(me, pos, 0.0f, capacity - 1, draw);
      }
  • Related