Thread: Vectors of classes

  1. #1
    Registered User
    Join Date
    Oct 2006
    Posts
    26

    Vectors of classes

    I have a class Monster for NPCs and so on in the game, and I want to create a vector containing all the currently active monsters. As they die or get spawned I figure I'll pushback or popback them in or out of the vector.

    Anyway, to test that it works I set up the following:


    Code:
    #include <iostream>
    #include <vector>
    
    void drawimage(SDL_Surface *srcimg, int sx, int sy, int sw, int sh, SDL_Surface *dstimg, int dx, int dy, int alpha);
    void render();
    Monster Monster1;
    Monster Monster2;
    vector<Monster> MonsterList (30);
    
    ...
    
    
    int main(int argc, char *argv[])
    {
    
        MonsterList[0]=Monster1;
        MonsterList[1]=Monster2;
    
    ...
    
    render()
    
    }
    
    void render()
    {
    ...
    
        drawimage(image, 0, 0, MonsterList.at(0).spritew, MonsterList.at(0).spriteh, screen, MonsterList.at(0).xpos-screenx, MonsterList.at(0).ypos-screeny, 255);
        drawimage(image, 0, 0, MonsterList.at(1).spritew, MonsterList.at(1).spriteh, screen, MonsterList.at(1).xpos-screenx, MonsterList.at(1).ypos-screeny, 255);
    }
    Drawimage is just a function that uses SDL to blit the monster graphic to the screen. I'm passing it the x,y coordinates and height/width of each Monster.

    Basically when I referenced Monster1 and Monster2 directly everything was fine, but when I put the vector in and referenced them from the vector I don't get any error but the monsters don't display on screen.

    I tried a few different combinations, I tried using pushback originally instead of an array reference but that didn't work either, the interesting thing though is that if I put the two MonsterList[0]=Monster1 lines into the render step it works. Is this some kind of scope issue?

    The reason I'm passing the individual components of Monster rather than the whole object is that I also pass things to drawimage() that aren't Monsters or even related classes.

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Not sure why your code as posted doesn't work - looks about right to me, but there are lots of bits missing, which could be the cause of the error, perhaps?

    If you are planning to have different kinds of monsters, you probably want a vector of pointers to the base-monster class and use virtual methods to achieve the interactions.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    Are your Monster's copy constructor and copy assignment operator working properly?

    >> Is this some kind of scope issue?
    Maybe, but we wouldn't be able to tell with the posted code. If you have more than one variable named MonsterList or Monster1 or Monster2 in your program, then you might have a problem there. For example, if a Monster1 exists inside main, maybe that is getting added instead of the global one. Or if there is another MonsterList inside main, it gets items assigned instead of the global one.

  4. #4
    Registered User
    Join Date
    Oct 2006
    Posts
    26
    Gah... I think I figured it out. I went to copy more code across but then looking at it it occurred to me the monsters were assigned to the vector before I specified their positions. I pasted it just after and they appear fine. Although they don't move - this must be because their assignment occurs before the main loop even begins so their position never changes.

    A better representation of the code I guess would be:

    Code:
    #include <iostream>
    #include <vector>
    
    void drawimage(SDL_Surface *srcimg, int sx, int sy, int sw, int sh, SDL_Surface *dstimg, int dx, int dy, int alpha);
    void render();
    
    class Creature {
        public:
          Creature() {};
          ~Creature() {};
          int spritew, spriteh;
          signed int xpos, ypos, xvel, yvel;
          int maxvel;
    };
    
    class Monster : public Creature {
        public:
          Monster() {
                    spritew=60;
                    spriteh=60;
                    maxvel=5;
                    };
          ~Monster() {};
          void MonsterAI();
          void MoveMonster();
    };
    
    Monster Monster1;
    Monster Monster2;
    vector<Monster> MonsterList (30);
    
    bool mainloop = true;
    
    Monster1.xpos = 500;
    Monster1.ypos = BG_H - 300;
    Monster1.xvel = 0;
    Monster1.yvel = -Monster1.maxvel;
    
    Monster2.xpos = 900;
    Monster2.ypos = BG_H - 200;
    Monster2.xvel = 0;
    Monster2.yvel = -Monster2.maxvel;
    
    ...
    
    
    int main(int argc, char *argv[])
    {
    
    MonsterList[0]=Monster1;
    MonsterList[1]=Monster2;
    
    ...
    
    
    while (mainloop) { game code - in which the monsters move around }
    
    ...
    
    render()
    
    }
    
    void render()
    {
    ...
    
        drawimage(image, 0, 0, MonsterList.at(0).spritew, MonsterList.at(0).spriteh, screen, MonsterList.at(0).xpos-screenx, MonsterList.at(0).ypos-screeny, 255);
        drawimage(image, 0, 0, MonsterList.at(1).spritew, MonsterList.at(1).spriteh, screen, MonsterList.at(1).xpos-screenx, MonsterList.at(1).ypos-screeny, 255);
    }
    It's quite long now so I've tried to still prune it right down to the important parts.

    So I guess I didn't really understand how vectors work. I just assumed that once I stored a monster in the vector then anything I do to the monster will be reflected in that entry in the vector... but it's more like it creates a copy of the monster at that point in time and puts it in that slot of the vector - is that right?

    Do I need to put the monsters into the vector after I've finished running all alterations on the monsters for that frame? That seems kind of tricky. I'd have to pull the monster out and replace it with the updated monster. Is there a better way to do it?

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    Quote Originally Posted by Dondrei
    So I guess I didn't really understand how vectors work. I just assumed that once I stored a monster in the vector then anything I do to the monster will be reflected in that entry in the vector... but it's more like it creates a copy of the monster at that point in time and puts it in that slot of the vector - is that right?
    Yes, the vector stores a copy of what you insert into it.

    Quote Originally Posted by Dondrei
    Do I need to put the monsters into the vector after I've finished running all alterations on the monsters for that frame? That seems kind of tricky. I'd have to pull the monster out and replace it with the updated monster. Is there a better way to do it?
    You could populate the vector, and then call the functions of those monster objects stored in the vector.

    That said, take another look at what matsp wrote in post #2, especially this part:
    Quote Originally Posted by matsp
    If you are planning to have different kinds of monsters, you probably want a vector of pointers to the base-monster class and use virtual methods to achieve the interactions.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  6. #6
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Right, so one solution would be what I suggested earlier: That you store a pointer (Elysia will probably suggest that you use a safe-pointer, e.g. shared_ptr<>) to the monster, so that what is stored in the vector is the ADDRESS of the original monster, rather than storing a copy of the monster in the vector.

    It's either that, or modify the vector-content rather than the original monster.

    Edit: Laserlight beat me to it...

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  7. #7
    Registered User
    Join Date
    Oct 2006
    Posts
    26
    Sorry... I'm a dork, I wasn't thinking clearly. The problem is that in my movement code I was still referring to Monster1 and Monster2, I should've changed that to MonsterList.at(0) and MonsterList.at(1)... anyway I just did that and presto.

    Also changed:

    MonsterList[0]=Monster1;
    MonsterList[1]=Monster2;

    to:

    MonsterList.push_back(Monster1);
    MonsterList.push_back(Monster2);

    Probably a better way to do it. Thanks for all your help.

  8. #8
    Registered User
    Join Date
    Oct 2006
    Posts
    26
    Oh, looks like you all figured it out right after I posted :P

    I'm planning to have different kinds of monsters, but Monster will be the base class, I think all the basic features of the monster will be defined at this level (certainly things like position and velocity and so on), if I have classes underneath Monster they'll probably just divide up monsters that shoot from monsters that don't or something. So I think this way makes sense... what do you think?

    Or is the pointer method more efficient?

  9. #9
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Dondrei View Post
    Oh, looks like you all figured it out right after I posted :P

    I'm planning to have different kinds of monsters, but Monster will be the base class, I think all the basic features of the monster will be defined at this level (certainly things like position and velocity and so on), if I have classes underneath Monster they'll probably just divide up monsters that shoot from monsters that don't or something. So I think this way makes sense... what do you think?

    Or is the pointer method more efficient?
    If you have classes that inherit from a base-class, you should definitely use pointers, as you will otherwise suffer from "slicing". You will also need to make all functions of the monster into virtual classes.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  10. #10
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by matsp View Post
    You will also need to make all functions of the monster into virtual classes.
    I think you mean virtual functions.
    Perhaps even make the pure virtual.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  11. #11
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Elysia View Post
    I think you mean virtual functions.
    Perhaps even make the pure virtual.
    Yes - fingers typing without brain involved ... [Another reason I don't give help in PM's is that when I type something wrong, the helpee is not getting it corrected by someone who knows roughly what I mean].

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  12. #12
    Registered User
    Join Date
    Oct 2006
    Posts
    26
    Hmm, now I have an elementary question... I made a loop for the two drawimage calls:

    Code:
        for (int i=0; i<=1; i++) {
            drawimage(block, 0, 0, MonsterList.at(i).spritew, MonsterList.at(i).spriteh, screen, MonsterList.at(i).xpos-screenx, MonsterList.at(i).ypos-screeny, 255);
        }
    Which works, so then I generalised to the size of the vector:

    Code:
        for (int i=0; i<=MonsterList.size()-1; i++) {
            drawimage(block, 0, 0, MonsterList.at(i).spritew, MonsterList.at(i).spriteh, screen, MonsterList.at(i).xpos-screenx, MonsterList.at(i).ypos-screeny, 255);
        }
    And my compiler gave me a warning about comparison of signed and unsigned integer expressions. Which I thought was weird because I assumed the size method would return an int. Maybe I'm not really understanding what I'm doing. I tried:

    Code:
        int x = MonsterList.size() - 1;
        for (int i=0; i<=x; i++) {
            drawimage(block, 0, 0, MonsterList.at(i).spritew, MonsterList.at(i).spriteh, screen, MonsterList.at(i).xpos-screenx, MonsterList.at(i).ypos-screeny, 255);
        }
    And that also works fine. Am I doing something wrong?




    I took a look into virtual functions, sounds interesting, I'm already kind of doing something similar because I have an Avatar class that's also derived from Creature. Both Avatar and Monster have a movement method but they aren't defined through Creature, I just created a moveavatar() in Avatar and a movemonster() in Monster. What's the difference between doing that and creating a virtual move() in Creature and then defining it differently in Monster and Avatar? I mean, the names were just what I happened to pick, if I renamed both moveavatar() and movemonster() to move() what would the difference be then?

    Thanks for all your help.

  13. #13
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Just make i an unsigned int (or more correctly vector<monster>::size_type).

    As an aside, I'd like to see the long line of drawimage to be modified. I'm not sure if a monster should know how to draw itself (that may be a good option), or if you simply should make a function that takes a parameter of a monster to be drawn. But certainly repeating a lot of MonsterList.at(i).*** is not right here, I'd say.

    Virtual functions have the benefit over regular functions that the TYPE of the object at the point of calling doesn't have to be know exactly - say for example we have:
    Code:
    class creature
    {
       public:
          virtual void move() { .... implement basic move variant ... }
    };
    
    class monster: public creature
    {
       public:
          virtual void move() { ... do move for monsters ...  }
    };
    
    class friendly: public creature
    {
       public:
          virtual void move() { ... do move for friendly ..  }
    };
    
    
    vector <creature *> CreatureList;
    
    SomeFunction()
    {
    ...
       Creature *p = new Monster;
       ... 
       CreatureList.PushBack(p);
       ... 
       p = new Friendly;
      
       CreatureList.PushBack(p);
    
    }
    ...
    DrawCreatures()
    {
       ... 
       for(i = 0; i < CreatureList.Size(); i++)
       {
           CreatureList[i]->move();
       }
       .. 
    }
    Now, CreatureList is a list of creatures. Without virtual, it would call creature::move(), rather than monster::move or friendly::move.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  14. #14
    Registered User
    Join Date
    Oct 2006
    Posts
    26
    Even if I change "int i" to "signed int i" I still get comparison of signed and unsigned integer expressions. All the example code I've seen just uses something like:

    Code:
    for(int i=0;i < Vector.size(); i++) {}
    But it throws a warning for me. From what I read size() should be of type size_type, which is an unsigned int. Anyway, I set it to:

    Code:
        for (vector<Monster>::size_type i=0; i<MonsterList.size(); i++) {
            drawimage(block, 0, 0, MonsterList.at(i).spritew, MonsterList.at(i).spriteh, screen, MonsterList.at(i).xpos-screenx, MonsterList.at(i).ypos-screeny, 255);
        }
    And it works... but I'm wondering why that was necessary. Is it just my compiler being overly fussy? I guess I could just ignore the warning.



    If you want to see it drawimage is:


    Code:
    void drawimage(SDL_Surface *srcimg, int sx, int sy, int sw, int sh, SDL_Surface *dstimg, int dx, int dy, int alpha=255)
    {
        //Exit if no image or completely transparent.
        if ((!srcimg) || (alpha == 0)) return;
    
        //Source rectangle.
    	src.x = sx;
    	src.y = sy;
        src.w = sw;
        src.h = sh;
    
    	//Destination rectangle.
    	dest.x = dx;
    	dest.y = dy;
    	dest.w = src.w;
    	dest.h = src.h;
    
        //Set alpha level.
        if (alpha != 255) SDL_SetAlpha(srcimg, SDL_SRCALPHA, alpha);
    
     	//Blit to the backbuffer.
    	SDL_BlitSurface(srcimg, &src, dstimg, &dest);
    }
    See, this part of the code started out life as the basic SDL blitting code you see in SDL's tutorials and in a lot of game development forums, I haven't altered much of it since I brought it in. So basically the code was originally non-object oriented (procedural?) and I've been progressively working classes into it because I've never done object oriented programming before and figured it would be a good way to learn.

    So at the moment I'm just passing bits of the classes to the functions - partly it's because I pass other things to the blitter apart from Creatures, like the background for instance. I guess the paradigm-correct thing to do would be to create a class above Creature, like Sprite or something, from which Creature can inherit height, width and position. Then the background and other sprites can be of the Sprite class, and I can pass them by reference to drawimage.

    Quote Originally Posted by matsp View Post
    Now, CreatureList is a list of creatures. Without virtual, it would call creature::move(), rather than monster::move or friendly::move.
    Oh right, so the method in the parent class takes precedence? I wondered what would happen in that case.

    You're right, I should probably unify the move methods with virtual functions.

  15. #15
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    Quote Originally Posted by Dondrei
    Even if I change "int i" to "signed int i" I still get comparison of signed and unsigned integer expressions.
    Obviously, since std::vector<Monster>::size_type is an unsigned integer type, but you changed i to be a signed int. Rather, just use std::vector<Monster>::size_type, as you did in your second example.

    Quote Originally Posted by Dondrei
    Oh right, so the method in the parent class takes precedence? I wondered what would happen in that case.
    Not quite. Suppose you have a pointer to creature that actually points to a monster object, with creature being a public base class of monster. If you invoke a virtual member function via this pointer, then the actual function that is called is the one from the monster class. If you invoke a non-virtual member function, then the actual function that is called is the one from the creature class, because the pointer is a pointer to creature. If the pointer was a pointer to monster, the member function from the monster class would be called, whether or not the member function is virtual.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. vector of vectors containing classes
    By larne in forum C++ Programming
    Replies: 3
    Last Post: 01-13-2009, 07:19 AM
  2. classes and vectors
    By izuael in forum C++ Programming
    Replies: 10
    Last Post: 11-27-2006, 04:19 PM
  3. Vectors and custom classes
    By cunnus88 in forum C++ Programming
    Replies: 16
    Last Post: 05-12-2006, 05:11 AM
  4. vectors and classes
    By jimothygu in forum C++ Programming
    Replies: 3
    Last Post: 04-27-2003, 07:53 PM
  5. How To use vectors for custom classes
    By johnnyd in forum C++ Programming
    Replies: 14
    Last Post: 03-25-2003, 10:04 PM