Question

Long story short: The program I'm working on is a rogue like - even though that isn't really needed for this question.

Here's the hierarchy tree for my classes related to this question:

                                     Entity
                        Item                     Creature
                  Weapon     Armor     

I have several virtual functions declared in Entity, which are also virtual in the classes derived from it.

I'm not sure how to word my question, but I'll explain the problem and post the code below. I have a factory type class, called ItemFactory, that opens an xml file and uses a simple parser that I made - and creates Item objects and sets their values. It has a method that returns an Item pointer. In my main file, I declare/define an ItemFactory object. When an Item needs to be dropped in the game, I use a pointer, that is of type, Item, and call the method to randomly choose an Item to point to. All of this works perfectly..

Here's the problem. Entity has a virtual method called dumpObject() which prints the state of the variables it has. dumpObject() is also virtual in Item. When it's called from an Item, the method first calls Entity's dump with this:

                 Entity::dumpObject();

Then it dumps it's own variables.. I do the same for Weapon and Armor except using this:

                 Item::dumpObject();

My Question:

Since the ItemFactory holds both - Weapons and Armor, and the pointer in the main program points to an Item, shouldn't calling "itemPointer->dumpObject();" dump the values for Weapon/Armor (depending which it is pointing to..) which would also dump the values in Item which would also dump the values in Entity?

When I run the code, the only part that gets dumped is the part in Item and Entity.

Let me know if I need to provide more detail. Any suggestions? Thanks!

Here's the Code Snippets

I have the headers included, just tried to minimize the amount of code that I'm posting


Item.cpp

void Item::dumpObject(){
    cout << "Item:" << endl;
    dumpObjectData();
}

void Item::dumpObjectData(){
    Entity::dumpObjectData();
    cout << "        [Weight] " << getWeight() << endl;
    cout << "         [Value] " << getValue() << endl;
    cout << "      [Quantity] " << getQuantity() << endl;
    cout << "   [Enchantment] " << getEnchantment() << endl;
}

Entity.cpp

void Entity::dumpObject(){
    cout << "Entity:" << endl;
    dumpObjectData();
}

void Entity::dumpObjectData(){
    XMLSerializable::dumpObjectData(); //XMLSerialization handles parsing

    cout << "          [Name] " << getName() << endl;
    cout << "   [DisplayChar] " << getDisplayChar() << endl;
    cout << "    [Properties] " << endl;

    for( auto it = m_vProperties.begin(); it != m_vProperties.end();it++ ){
            cout << "       - " << (*it) << endl;
    }
}

Weapon.cpp

void Weapon::dumpObject(){
    cout << "Weapon:" << endl;
    dumpObjectData();
}

void Weapon::dumpObjectData(){
    Item::dumpObjectData();

    cout << "        [Damage] " << getDamage() << endl;
    cout << "         [Range] " << getRange() << endl;
    cout << "      [Accuracy] " << getAccuracy() << endl;
    cout << "      [AmmoType] " << getAmmoType() << endl;
    cout << "          [Type] " << getType() << endl;
}

Armor.cpp

void Armor::dumpObject(){
    cout << "Armor:" << endl;
    dumpObjectData();
}

void Armor::dumpObjectData(){
    cout << "calls to dump item data"<<endl;
    Item::dumpObjectData();
    cout << "calls to dump armor"<<endl;
    cout << "          [Type] " << getType() << endl;
    cout << "          [Slot] " << getSlot() << endl;
    cout << "    [ArmorValue] " << getArmorValue() << endl;

}

Main

    ItemFactory myItems = ItemFactory::instance();
    Item * pItem1 = myItems.generateItem();
    pItem1->dumpObject();



Headers

Entity.h

#include "XMLSerializable.h"
#include <vector>

class Entity : public XMLSerializable {

public:
    Entity(void);
    virtual ~Entity(void);

    virtual void dumpObject();
    virtual void dumpObjectData();

};

Item.h

#include "Entity.h"

class Item : public Entity{

public:
    Item(void);
    virtual ~Item(void);

    virtual void dumpObject();
    virtual void dumpObjectData();

};


Weapon.h

#include "Item.h"

class Weapon : public Item {
public:
    Weapon(void);
    virtual ~Weapon(void);

    virtual void dumpObject();
    virtual void dumpObjectData();
   
};

Armor.h

#include "Item.h"

class Armor : public Item {
public:
    Armor(void);
    virtual ~Armor(void);

    virtual void dumpObject();
    virtual void dumpObjectData();

};

ItemFactory.cpp

ItemFactory & ItemFactory::instance(){
    static ItemFactory myObj;
    return myObj;
}

ItemFactory::ItemFactory(){
    m_mtRandom.seed( time(NULL) );
    fstream xmlFile;
    xmlFile.open("items.xml");
    vector<XMLSerializable*> pObjects;
    parseXML(xmlFile, pObjects);

    XMLSerializable * pObject;

    for(auto it = pObjects.begin(); it != pObjects.end(); it++){
            pObject = (*it);
            Item * pItem = dynamic_cast<Item*>(pObject);
            if (pItem != NULL){
                    m_vItems.push_back(pItem);
            }
    }
}


ItemFactory::~ItemFactory(){

}


Item * ItemFactory::generateItem() {
    vector<Item*> tempItems;

    for(auto it = m_vItems.begin(); it != m_vItems.end(); it++){
            tempItems.push_back((*it));
    }

    int randomItem = (m_mtRandom() % (m_vItems.size() - 1));


    Item * pItem = tempItems.at(randomItem);

    Item * pReturnValue = new Item(*pItem);

    return pReturnValue;
}

Now that I just did all that work, I don't think any of the code except maybe Main was necessary.. Lol I'm guessing my logic for the pointer in Main is wrong?

Was it helpful?

Solution

Well here's your problem:

Item * pReturnValue = new Item(*pItem);

This is giving you a shallow copy so that you do not get an Armor or Weapon.

If you need to do a copy given only a base class instance, define a clone method in the base class.

It looks like you are trying to use the prototype pattern, so you do want to create new instances?

class Entity : public XMLSerializable {

public:
    Entity(void);
    virtual ~Entity(void);

    virtual Entity* clone() const { return new Entity(*this);}

    virtual void dumpObject();
    virtual void dumpObjectData();

};

class Armor : public Item {
public:
    Armor(void);
    virtual ~Armor(void);
    virtual Armor* clone() const { return new Armor (*this);}
    virtual void dumpObject();
    virtual void dumpObjectData();

};

Note the use of covariant return values for clone(). I.e. The return values do differ but as the method signature matches and the return values are derived from one another, the call is virtual.

You then can write:

Item * pReturnValue = pItem->clone();

See: Wikipedia for background on the Prototype pattern.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top