Reputation: 554
I have quite a blocking problem which I don't understand. Generally, I'm making a kind of anthill simulation. There is one main passage inside divided into segments and from some of the segments ants can enter into chambers. All of these three classes (Passage, Segment, Chamber) have one common thing - collection of ants currently visiting them. So there is an abstract class AntHolder
, which contains vector<Ant*>
(only members relevant to the case are shown):
class AntHolder
{
protected:
std::vector<Ant*> ants;
/* some other members here */
public:
virtual bool antEnter(Ant* ant) = 0;
/* some other functions here */
};
The antEnter
function is implemented differently in derived classes, but generally serves the purpose of adding ant to the ants
. From the derived classes particularly I'm interested in AntChamber
class (here also less important members were omitted):
class AntChamber : public AntHolder
{
protected:
int itemCapacity;
int additionalCapacity;
std::vector<Item*> items;
bool hasFood;
bool hasEgg;
public:
bool putItem(Item* item);
virtual bool antEnter(Ant* ant);
};
The putItem
function is similar to the antEnter
function, but it adds Item
objects to items
collection. (Items are e.g. food, which are moved by ants from one chamber to another.) Below there is shown implementation of both functions:
bool AntChamber::antEnter(Ant* ant)
{
if (items.size() + ants.size() == itemCapacity + additionalCapacity) return false;
ants.push_back(ant);
return true;
}
bool AntChamber::putItem(Item* item)
{
if (items.size() == itemCapacity ||
items.size() + ants.size() == itemCapacity + additionalCapacity)
return false;
if (item->getItemKind() == Food) hasFood = true; // Food == enum value
else if (item->getItemKind() == Egg) hasEgg = true; // Egg == enum value
items.push_back(item);
return true;
}
You can clearly see, that they're almost identical. But when it comes to their effect, there is crucial, surprising difference, and that is the core of my problem.
Let's say I already have an AntChamber* chamber
constructed. When I run the following piece of code:
Item* item = new Item(Food);
chamber->putItem(item);
, then after that both item
and chamber->items.back()
points to some memory with this object. But when I run the analogous piece of code:
Ant* ant = new Ant(chamber);
chamber->antEnter(ant));
, then after that ant
point to the object, but chamber->ants.back()
points to NULL!
I absolutely can't understand what is going on, especially, that both putItem
and antEnter
in fact do the same thing: push_back
the pointer, which was passed by parameter. I have already tried to simulate such case in some simplier code, like:
class A { };
class B { };
class C
{
vector<A*> va;
vector<B*> vb;
public:
A* vaBack() { return va.back(); }
B* vbBack() { return vb.back(); }
void addA(A* a) { va.push_back(a); }
void addB(B* b) { vb.push_back(b); }
};
int main(int argc, char** argv)
{
A* a = new A();
B* b = new B();
C* c = new C();
cout << (unsigned int)a << endl;
c->addA(a);
cout << (unsigned int)c->vaBack() << endl;
cout << (unsigned int)b << endl;
c->addB(b);
cout << (unsigned int)c->vbBack() << endl;
delete c;
delete b;
delete a;
}
, but it appears to work just fine - none of pointers is 0x000000.
Upvotes: 0
Views: 181
Reputation: 554
Oh My God, I'm sooo blind...
I did SSCCE, as Shafik Yaghmour advised and I noticed the problem while doing it.
I used a mental leap, saying, that chamber->items.back()
or chamber->ants.back()
are NULL, because in fact, they weren't! But they're protected in their classes, so I wrote in both classes a function to get to i-th item/ant. The problem was this function. It made a standard idiotproof protection against giving index out of vector's bonds, but made a mistake doing so:
if (idx < 0 || ants.size() >= idx) return 0; // SHOULD BE <= !!!
return ants[idx];
so it always returned 0... And I looked at this method maybe hundreds of times when looking for this problem, and never noticed anything wrong (till now).
Stupid mistake... Thank you very much Shafik.
Upvotes: 1