| |||||||||
![]() |
|
|
«
Previous Thread
|
Next Thread
»
|
Thread Tools | Search this Thread | Display Modes |
|
|
|
Ajax Application Generator Generate database and reporting .NET Web apps in minutes. Quickly create visually stunning, feature-rich apps that are easy to customize and ready to deploy. Download Now!
|
|
#1
|
|||
|
|||
|
Classes - Downcasting issues
Hey guys, I'm trying to make an RPG game, and I'm having problems with downcasting. So I've got a class, Item, and a couple inherited classs, Weapon / Armor (two different classes). Now, I've tried the following statements, and have received the following results:
Original Weapon: Name - A Sword 1d2 } Attributes of the Appearance - '|' } Item Class note: the name is a string, that is created by a method in the weapon class (based on the weapon type, die and sides) Type 0 } Sides - 2 } Attributes of the Die - 1 } Weapon Class Code:
Item* i = &t->item.at(0); // This will return an item, that we know (in these testing cases) is a weapon ::Weapon* w1 = (Weapon*)i; // Results // Name: A Sword 1d2 // Appearance: '|' // Type: - 4429968 // Sides: 0 // Die: -842203136 ::Weapon* w2 = i; // Results // Error, doesn't work ::Weapon* w3 = dynamic_cast<Weapon*>(i); // Results // Name: Expression cannot be evaluated // Appearance: Expression cannot be evaluated // Type: Expression cannot be evaluated // Sides: Expression cannot be evaluated // Die: Expression cannot be evaluated ::Weapon* w4 = static_cast<Weapon*>(i); // Results // Name: A Sword 1d2 // Appearance: '|' // Type: - 4429968 // Sides: 0 // Die: -842203136 ::Weapon* w5 = reinterpret_cast<Weapon*>(i); // Results // Name: A Sword 1d2 // Appearance: '|' // Type: - 4429968 // Sides: 0 // Die: -842203136 The weapon inherits from an item, Code:
class Weapon : public Item
{
...
};
and the Item is just some normal class, Code:
class Item
{
...
};
I'm a huge noob to C++, I come from C# instead. Could anybody help me out with this and tell me what's going on, and what I can do to fix this? It'd be greatly appreciated, thanks so much (btw, I've asked this on another forum board, and someone had suggested writing all weapon and item types to a text file, and reading the information there - although I do appreciate his/her help, I would much rather keep the information in here for now). Thanks guys. |
|
#2
|
||||
|
||||
|
Ok first of all, if you need to downcast explicitly you probably already have a flawed/non optimal design. In c++ you should try to avoid explicit casting.
Second, if you must do a polymorphic downcast then prefer to use dynamic_cast. Unless you are absolutely sure and want to avoid the overhead of dynamic_cast, then you can use static_cast. But you should only do that in performance critical code. Now in your case we need to see more code to see what is going wrong. I am _guessing_ that your Item container is storing the objects by value instead of storing pointers. So you are experiencing the fabled c++ slicing where you 'cut off' the parts of the derived class and only keep the base class parts. Can you show us the full code for the Weapon/Item class plus the calling code and the container?
__________________
Current project: roborally |
|
#3
|
|||
|
|||
|
Sure things..
Code:
// Item.h
#ifndef ITEM_H
#define ITEM_H
#include <string>
#include <sstream>
#include <stdlib.h>
using namespace std;
class Item
{
public:
// Constructor / Destructor
//Item();
Item(string name = "Nothing", int weight = 0, int value = 0, bool stackable = false, char appearance = ' ');
virtual ~Item(); // Destructor is different for armor/weapon/etc.
bool Inventory; // On the map or in an inventory
bool wielded; // Is this is wielded?
string name; // Name of the item
int weight; // Weight of the item
int value; // What is the value of the item (useful for considering if a monster should pick up the item or not)
bool stackable; // Is the item stackable?
char appearance; // Appearance of the item
int itemType; // -1 None, 0 Weapon, 1 Armor
private:
};
#endif
Code:
// Weapon.h
#ifndef WEAPON_H
#define WEAPON_H
#include <string>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <sstream>
#include "Item.h"
using namespace std;
class Weapon : public Item
{
public:
enum weaponType
{
None,
Sword,
Axe,
Mace,
Bow,
Sling
};
Weapon();
Weapon(Item i);
Weapon(Item i, Weapon::weaponType type, int dice, int sides, int dmg = 0, int hit = 0, int multiplier = 1, float crit = 0, string specialName = "");
~Weapon();
int GetHit(); // Get the weapon's hit (this is not usually the total that the character hits for)
weaponType type; // Weapon type
int dice; // Number of die
int sides; // Number of sides of the dice
int dmg; // to-dmg bonus (added onto character's total to-dmg)
int hit; // to-hit bonus (added onto character's total to-hit)
int multiplier; // How many times to hit
float crit; // Chance to critical strike
string specialName; // This is for names such as "Golem's dagger" where Golem's would be the name
private:
void SetName(); // Set the name of the weapon
string WeaponTypeString();
};
#endif
Code:
::Weapon tmpW(Item(), Weapon::weaponType::Sword, 1, 2); ::Armor tmpA(Item(), Armor::armorType::Chest, Armor::armorMaterial::Leather, 1); tmpE.character.e.melee = tmpW; tmpE.character.e.chest = tmpA; Here we create a weapon and armor, and then equip it to the enemy character (Each character will have a melee slot (weapon), a chest slot (armor), and a few inventory slots (item)). Code:
// Drop the weapon
if (e.at(inTheWay).character.e.melee.name != "None")
{
Weapon w = e.at(inTheWay).character.e.melee;
w.Inventory = false;
w.wielded = false;
this->items.push_back(w);
t->GetItem(w, (int)this->items.size() - 1);
}
// Drop the armor
if (e.at(inTheWay).character.e.chest.name != "None")
{
Armor* a = &e.at(inTheWay).character.e.chest;
a->Inventory = false;
a->wielded = false;
this->items.push_back(*a);
t->GetItem(*a, (int)this->items.size() - 1);
}
We start by checking if the enemy is holding a weapon or armor piece, and if so we create a variable (one is just a normal variable, and the other is a pointer - this is just for testing cases to see if one works and the other doesn't). Next we put this item onto the tile that the enemy was standing on (pushing it into the vector of items). Code:
character->character->PickupItem(t->RemoveItem(t->items.size() - 1));
Item Map::Tile::RemoveItem(int index)
{
this->itemIndex.erase(this->itemIndex.begin() + index);
Item i = this->items.at(index);
this->items.erase(this->items.begin() + index);
return i;
}
void Character::PickupItem(Item item)
{
if (!this->i.inventoryFull)
i.AddItem(item);
}
After the character walks onto the tile and says yes to picking up the item, we come to this statement.. Code:
if (c.i.slots.at(slot - 'a').itemType == 0)
{
::Weapon w = (Weapon)c.i.slots.at(slot - 'a');
if (c.e.melee.name == "Nothing")
{
Item item = c.e.melee;
c.dmg -= c.e.melee.dmg;
c.hit -= c.e.melee.hit;
c.e.melee = w;
c.dmg += c.e.melee.dmg;
c.dmg += c.e.melee.hit;
c.i.slots.at(slot - 'a').~Item();
c.i.slots.at(slot - 'a') = item;
}
else
{
c.e.melee = w;
c.dmg += c.e.melee.dmg;
c.dmg += c.e.melee.hit;
c.i.slots.at(slot - 'a').~Item();
}
}
And this is the main code, where we wield the weapon. You can only see what statement that I used to convert the item to a weapon (this is because I've done that testing much earlier, but I thought it'd be more useful to show you how my program is set up). I know this all may look very messy and noobish :$ bare in mind I'm very new to C++. Anyways, thanks so much for the help =) I really appreciate it. |
|
#4
|
||||
|
||||
|
Ok one big difference between C++ and c# (or java) is that c++ uses value semantics instead of reference semantics. You should have a look at this link for more information:
http://www.parashift.com/c++-faq-li...-semantics.html You still have not shown how you create your container of items but I am pretty sure you are experiencing slicing. See a little explanation of it here It comes down to the fact that you probably need to store a pointer to Items in your Item container instead of storing them by value. Even in that part where you are dropping the armor and using a pointer just for testing, you are push_back *a, which means you are not storing a pointer to the object but a sliced copy of it. I hope this gets you on your way a bit, if not, show me your Item container and I may be able to give you some more specific advice. You are experiencing slicing in many other places too by the way, and you should almost never called a destructor directly ( c.i.slots.at(slot - 'a').~Item(); ) but only through delete. |
|
#5
|
|||
|
|||
|
Hey again, so here's my containers:
Code:
// This is for the characters inventory
std::vector<Item> slots;
struct Equipment
{
Weapon melee;
Armor chest;
};
// This is the container for the tile's items
std::vector<Item> items; // All of the items on this tile
I tried simply changing everything from a value to a pointer, but I don't think that will work: what if I create a weapon and armor piece in the constructor for the map class (for one of the monsters in the map) and equip it to the monster. Wouldn't the weapon be gone at the end of the method? and if not, wouldn't it be gone after the monster dies, the item is put into the tile and the monster is deleted? My only other guess is to create a vector of items within the whole gameloop and have all the items, whether they are in the character class or the tile class or the map class, point to the items in the gameloop. Is this what I should be doing? I can't think of any examples but I don't see this working very well later on in the game. Anyways, thanks so much for your help - putting up with my noobishness. Really appreciate it =) |
|
#6
|
||||
|
||||
|
I see your point. A global vector of items is a solution but maybe not the most elegant. But still, even with a global vector of items you would still experience slicing. Seriously. You need to change the type of your vector to hold pointers to items, without that there is no polymorphic behaviour. You can create new objects using the new operator. The downside of that is that you will need to clean up the memory yourself but you could use a boost::shared_ptr for that. Boost::shared_ptr's work well with vectors. Alternatively you could use boost:
tr_vector. See http://boost.org.But still, you should try to grasp the problem of slicing first. vector<Item> is not going to work for derived classes of Item. |
![]() |
| Viewing: Dev Articles Community Forums > Programming > C/C++ Help > Classes - Downcasting issues |
| Thread Tools | Search this Thread |
| Display Modes | Rate This Thread |
|
|
|
|
|