Polymorphism/virtual function not working here...?

I have a class "Product" which is inherited by two sub-classes, "Customisable" and "Perishable". All classes are concrete and can be instantiated.
Product has a virtual function "purchase". It is overridden in Customisable but not in Perishable. Customisable's version actually call the superclass version, and then does a little bit extra: it prompts the user for a customisation string (e.g. the name you want engraved on the product, or something like that).
The purchase function is called outside the class, from a variable of type "Product". You would expect that, since it's a virtual function, if the value of the variable was of type Customisable, then the Customisable version of the function would be called. However, I only ever get the superclass function called, no matter what the object inhabiting the variable. I'm also having a similar problem with another function; however I won't go into that now, as it's probably due to the same cause.

Relevant code is below.

Product.h
1
2
3
4
5
6
7
8
9
class Product
{
    protected:
        ...
    public:
        ...
        virtual int purchase(Communicator * communicator);
        ...
};


Product.cpp (note that "Communicator" is used to communicate w/ the user)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int Product::purchase(Communicator * communicator)
{
    int amount = 0;
    string amountString;
    while(amount <= 0)
    {
        amountString = communicator->prompt("Quantity to purchase");
        amount = atoi(amountString.c_str());
        if(amount <= 0)
        {
            communicator->relayToClient("Invalid quantity");
        }
    }

    boost::mutex::scoped_lock productLock(productMutex);
    if(quantity - amount >= 0)
    {
        quantity -= amount;
        communicator->relayToClient("Item purchased!");
    }
    else
    {
        communicator->relayToClient("Insufficient quantity available. Item not purchased.");
    }

    return amount;
}


Customisable.h
1
2
3
4
5
6
7
8
9
class Customisable : public Product
{
    private:
        ...
    public:
        ...
        int purchase(Communicator * communicator);
        ...
};


Customisable.cpp
1
2
3
4
5
6
7
int Customisable::purchase(Communicator * communicator)
{
    int amount = Product::purchase(communicator);
    communicator->prompt(customisation); //This sends the "customisation" option to the user, and asks them to enter a string

    return amount;
}


CustomerService.cpp (this function calls the purchase function of the product)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void CustomerService::purchaseItem(Server * server)
{
    server->displayCatalog();
    
    string numberString;
    int number = 0;
    Product * product = NULL;
    while(product == NULL)
    {
        numberString = server->getCommunicator()->prompt("Product number");
        number = atoi(numberString.c_str());
        product = server->getCatalog()->getProduct(number);
        if(product == NULL)
        {
            server->getCommunicator()->relayToClient("Invalid product number");
        }
    }
    
    int amount = product->purchase(server->getCommunicator()); //THIS IS THE LINE THAT CALLS THE PURCHASE FUNCTION OF THE PRODUCT

    server->addToTotal(product, amount);
}


As I said, even if the "product" variable in the purchaseItem method holds a Customisable object, I will not get the prompt for the customisation string. Can anybody figure this out?
product = server->getCatalog()->getProduct(number);


is the catalog doing something weird like storing Products and not Product*s?

Can you post some code from the catalog? (specifically getProduct(), the declaration of whatever container it uses to store the products, and relevent code that modifies the products)
closed account (EzwRko23)
Warning: Bad design. Concrete method overriding a concrete method. Needs rethinking and refactoring.
Disch: It actually is storing Products and not Product*s. I didn't realise this inhibited polymorphism. I'll change it and see if that fixes the problem. Thanks very much.

Xorebxebx: Everything about your post makes you seem like a robot or an artifical intelligence of some sort. Try reading it in Robbie the Robot's voice and you'll see what I mean.
Disch: It actually is storing Products and not Product*s. I didn't realise this inhibited polymorphism


It does. Allow me to try to explain:

Since Customizable Customisable derives from Product, this means that it automatically has everything Product has, and a little bit extra that Product does not have. For example:

1
2
3
4
5
6
7
8
9
struct Parent
{
  int a;
};

struct Child : public Parent
{
  int b;
};


Here, Child has both a variable named 'a' (which it inherits from Parent), and it also has a variable named 'b' (which Parent does not have). On the other hand, Parent only has an 'a', and it does not have a 'b'.

You can explore this further like so:

1
2
cout << sizeof(Parent) << endl;  // will print 4  (probably)
cout << sizeof(Child) << endl;  // will print 8  (probably) 


Now what happens if you have something like the below:

1
2
3
4
5
Child c;  // a Child, 8 bytes in size

Parent p;  // a Parent, 4 bytes in size

p = c;  // <- ! 


That last line can't copy the full Child to 'p'. It can copy only the data that will fit in 'p'. Since p doesn't have a 'b', c.b can't be copied to p.b (because p.b doesn't exist).


So basically, if you have a Parent, then you only have a Parent. You do not have a Child.


Pointers, on the other hand, are different, because they do not need to store the whole object. The object is typically stored anonymously on the heap and the pointer simply points to it.

Therefore if you have a Parent*, it might be pointing to a Parent, but it could also be pointing to something larger than a Parent (like a Child).


Hopefully that makes sense =x
Disch: it makes perfect sense now. Thanks so much for the detailed explanation. I have changed the catalog to hold pointers and everything is working correctly now. Fortunately I only had to change a couple of functions to do so.

Thanks again.
Topic archived. No new replies allowed.