Why isn't this an infinite loop?

greenspun.com : LUSENET : Steve Heller's books : One Thread

This one has me truly stumped:
void StockItem::Reorder(ostream& os)
{
  m_Worker->Reorder(os);
}

void DatedStockItem::Reorder(ostream& os)
{
  if (m_Expires < Today())
    {
    os << "Return " << m_InStock << " units of " << m_Name;
    os << " with UPC " << m_UPC;
    os << " to " << m_Distributor << endl;
    m_InStock = 0;
    }
  StockItem::Reorder(os);
}
these were cut and pasted from your source code. Why don't these two form an infinite loop? From what I can see, they would. I have not compiled and run the program to test it, but I cannot see how this would not be a problem.

-- Mike Mannakee (B3FEETBACK@AOL.com), May 09, 1999

Answers

You're missing one piece of the puzzle

You're missing one function, namely UndatedStockItem::Reorder, which is what will be called by the StockItem::Reorder function, because the member variable pointed to by the m_Worker pointer in a DatedStockItem object is an UndatedStockItem. The UndatedStockItem Reorder function does NOT call StockItem::Reorder, so an infinite regress is avoided.

-- Steve Heller (stheller@koyote.com), May 09, 1999.

I don't understand why UndatedStockItem::Reorder is called

I'm still missing something here. If the code calls StockItem::Reorder(os), how does UndatedStockItem::Reorder get called? Wouldn't you have to explicitly call the undated version?

Or does calling a function of any base class from the same function in a derived class only move you up one leg in the hierarchy? Like a ladder, you have to go one rung at a time? I just don't get it, the code SAYS StockItem, I would think that class would have to be called. Shouldn't one simply call the Undated version directly?

-- Mike Mannakee (B3FEETBACK@AOL.com), May 09, 1999.


Virtual functions are tricky

UndatedStockItem::Reorder is called by the statement "m_Worker->Reorder(os);" in the StockItem::Reorder function, because the object pointed to by "m_Worker" is an UndatedStockItem object.

That's why Reorder is a virtual function: so that the correct version of Reorder will be called depending on the actual type of the object pointed to, rather than depending on the type of the pointer itself.

You see, the type of the object pointed to can depend on how it was created, as it does in the case of dated vs. undated objects. That's the whole point of virtual functions: to allow the correct version of a function to be called without having to have an "if" statement selecting it. This allows extensions to be added to the types handled without having to modify existing code.

By the way, you shouldn't feel bad if you have trouble understanding the purpose of virtual functions; that's probably the hardest lesson to learn when getting started with object-oriented programming.

-- Steve Heller (stheller@koyote.com), May 09, 1999.


tricky yes, but what is the type of a null pointer?

I got you. But I must not be clear on something here, or making myself clear. The initial code that got my dander up is in the DatedStockItem::Reorder function, not the UndatedStockItem::Reorder function. I think I understand this all pretty well so far, but let me walk through what I see happens on the application program's call to StockItem::Reorder()(which is the only one the programmer sees):

1. The StockItem::Reorder function is called, which just executes the line m_worker->Reorder(os);. 2. In the case of the worker being a DatedStockItem, that would of course call DatedStockItem::Reorder(ostream& os). 3. this funtion checks the date and if the product is expired outputs the fact that a return needs to be done and then 4. explicitly calls the funtion StockItem::Reorder(os);. 5. That would lead us back to 1. above.

I don't see how to escape this, or how UndatedStockItem::Reorder is ever called when the m_worker points to a DatedStockItem. My understanding so far leads me to believe that the last line as in 4. above should have to call UndatedStockItem::Reorder, as the type of the worker ...wait a minute, something just occurred to me while writing this. What I didn't get before was that the StockItem that the code in StockItem::Reorder was going to look at was NOT the one that had this DatedStockItem as it's m_worker,(its manager) but rather this worker's internal StockItem variable that had been initialized by the constuctor that takes an int as it's argument. That constructor always makes a m_worker that points to nothing, a zero. Is that how the code m_worker->Reorder(os); now somehow gets us to the UndatedStockItem::Reorder function? This really still isn't clear. I do now see, however, that:

6. the code m_worker->Reorder(os); now calls that function for the base class part of the DatedStockItem, which was initialized by the goofy constructor as a zero, a pointer to nothing.

can you fill me in on 7 & 8, whatever it takes to get to the code for the UndatedStockItem::Reorder now? I'm a little further along, but still don't see it. The type of a pointer that points to nothing isn't obvious to me, and I'm not following how a null pointer gives us the Undated function we want.

-- Mike M (B3FEETBAK@aol.com), May 11, 1999.


I stand corrected

You are absolutely right that this program wouldn't work. By some oversight, I didn't write a test program to make sure that the final version of the item classes would work. The correction is to call UndatedStockItem::Reorder rather than StockItem::Reorder in DatedStockItem::Reorder. I've posted the correction on the Who's Afraid of More C++? page, and have updated the ZIP file containing the source code. I've included a test program that exercises the Reorder function, called "itmtstp.cc", The RHIDE project for it, "itmtstp.gpr", is included in a new ZIP file containing all the RHIDE project files for the book.
I apologize for the confusion I've caused you. Thanks for pointing out the error!

-- Steve Heller (stheller@koyote.com), May 16, 1999.


Moderation questions? read the FAQ