Am i doing virtual functions right?

Pages: 12
Ok so i want to make a program where you manage a store (kind of like 711), and in the store theres pop machines and cash registers, refridgeration units slushi machines etc i have a store base clas and a pop machine derived class, and im going to be making other machines as well but im confused as to where i put my variables, they have to do with the pop machine so do i put them in the derived pop machine class? or do i just put them in the store base class?

store class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef STORE_H_INCLUDED
#define STORE_H_INCLUDED

#include <vector>

using namespace std;

class store
{
    public:
        virtual void MachineName() = 0;
        virtual void Products() = 0;
        virtual void ProductCosts() = 0;
        virtual void MachineType() = 0;
};

#endif // STORE_H_INCLUDED 


pop machine derived class

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
28
29
30
31
32
33
34
35
36
37
38
39
40
#ifndef POP_MACHINE_H_INCLUDED
#define POP_MACHINE_H_INCLUDED

#include "store.h"
#include <iostream>
#include <vector>

using namespace std;

class pop_machine : store
{
    private:
        string machine_name;
        vector<string> machine_products;
};

void store::MachineName()
{
    cout << "Please enter a name for this pop machine" << endl;
    getline(cin, machine_name);


}

void store::Products()
{
    cout << "Please enter a list of products for " << endl;
}

void store::ProductCosts()
{

}

void store::MachineType()
{

}

#endif // POP_MACHINE_H_INCLUDED 


Also do i need to put my functions outside of the pop machine class or can i put them inside.
Last edited on
Now that i think about it i dont really think i need the vector to store the names, just a string in the pop machine class? or could i do:

1
2
3
4
virtual void MachineName()
{
   string name;
}


??
Last edited on
The vector to store products is fine, but I certainly don't feel a pop machine should inherit a store though all machines could have a common base class. I think the store (7/11) should compose of several machines which can be stored in a vector of the base class.
well the pop machine is part of the store, and the store has different machines which will all have their own classes, or should i just make a machine base class and derive all of the machines from that? then what do i put in the store class?
Ok i did a little excersize with virtual functions and i want to know if im doing this right before i go any further.

main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <string>
#include <vector>
#include "store.h"
#include "pop_machine.h"
#include "machines.h"

using namespace std;


int main()
{
    machines *pmachine = new pop_machine;
    pmachine->MachineName();
    delete pmachine;

    return 0;
}


machines.h

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
28
29
30
31
#ifndef MACHINES_H_INCLUDED
#define MACHINES_H_INCLUDED

#include <string>
#include <vector>
#include <iostream>

using namespace std;

class machines
{
    protected:
        string machine_name;
        vector<string> products;

    public:
        virtual void MachineName()
        {

        }
        virtual void Products()
        {

        }
        virtual void ProductCosts()
        {

        }
};

#endif // MACHINES_H_INCLUDED 


pop machine.h

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#ifndef POP_MACHINE_H_INCLUDED
#define POP_MACHINE_H_INCLUDED

#include "machines.h"
#include <iostream>
#include <vector>

using namespace std;

class pop_machine : public machines
{
    private:
        //MachineName Vars
        int choice;
        bool satisfied;
        bool choice_loop;

        //Products Vars
        bool vectEntryLoopEnd;
        string enterProducts;

    public:
        void MachineName()
        {
            satisfied = false;
            choice_loop = false;

            while(satisfied != true)
            {
                while(choice_loop != true)
                {
                    cout << "Enter the brand name of the pop machine" << endl;
                    getline(cin, machine_name);

                    cout << "\nAre you satisfied with this name: " << machine_name << " ?" << endl;
                    cout << "1) Yes" << endl;
                    cout << "2) No" << endl;
                    cin >> choice;

                    if(choice == 1)
                    {
                        Products();
                        choice_loop = true;
                    }
                    else if(choice == 2)
                    {
                        cin.ignore(250, '\n');
                        break;
                    }
                }
            }
        }
        void Products()
        {
            vectEntryLoopEnd = false;

            cout << "Please enter the products " << machine_name << " will sell" << endl;
            while(vectEntryLoopEnd != true)
            {
                getline(cin, enterProducts);
                products.push_back(enterProducts);

                if(enterProducts == "Stop" || enterProducts == "stop")
                {
                    products.pop_back();
                    vectEntryLoopEnd = true;
                }
            }
            for(int i = 0; i < products.size(); i++)
            {
                cout << products[i] << endl;
            }
        }
        void ProductCosts()
        {

        }
};

#endif // POP_MACHINE_H_INCLUDED 
you can use typeid runtime operator to check if you're implementation is polymorphic or not.
Base classes should always have a virtual destructor.
Oh ok, other than that is everything else correct?
1# Trivial, but the base class of pop_machine should be machine rather than machines.

2# Some (all?) of the member variables in pop_machine look like they're only used within the scope of a single function. These should be removed and replaced with locals.

3# The method you have don't look pop machine specific, so should prob be refactored to the base class. In fact, as it stand, almost everything could be factored to the base class except the type of machine.

4#. You should ideally (from an OO purist point of view) make machine_name and products private and provide methods to access them (but not one which returns a vector<string>.) But this might not be needed after 3#.

5# Method names should say what they do: none of MachineName, Products, ProductCosts really do that. (You member variable names are ok; you could even drop the machine_ from machine_name of class machine as name makes sense in context.)

Andy
Last edited on
"3# The method you have don't look pop machine specific, so should prob be refactored to the base class. In fact, as it stand, almost everything could be factored to the base class except the type of machine."

I have a vague understanding of what you mean can you be a little more specific.

"5# Method names should say what they do: none of MachineName, Products, ProductCosts really do that. (You member variable names are ok; you could even drop the machine_ from machine_name of class machine as name makes sense in context.)"

I did have them named like that but there are different types of machines like a slurpie machine, a cash register, security camera, electronic safe etc that can use the same functions so wouldnt naming them a specific name defeat the purpose of re usability?
Here's my take, below.

Also, while not related to you orig. question (Am i doing virtual functions right?), your class was getting a bit heavy to be declared inline. And using namespace std should never be used in headers as it makes it hard to control where it's in force, so I've made those tweaks as well.

Also, to get the program to stop, I had to remove the choice_loop while loop.

In the end, there's not really a need for a base class/derived class split yet. Until you add some pop machine specific code. Though I can see the need for a vending machine between machine and pop_machine if you want machine to be the base class for other types of machines. Then a lot of machine's current code would end up in the intermediary class. But I'm not sure where security cameras fit into your model?

Andy

PS It would probably make sense to replace the product names and cost calls with a single method (product details?) which get the product name, cost, etc in one go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// main.cpp

#include <iostream>
#include "pop_machine.h"
using namespace std;

int main() {
    pop_machine pm;
    bool ok = pm.Configure();
    if(ok)
        pm.DisplayProducts();

    return 0;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// pop_machine.h

#ifndef POP_MACHINE_H_INCLUDED
#define POP_MACHINE_H_INCLUDED

#include "machine.h"

class pop_machine : public machine
{
    public:
        pop_machine() : machine("pop machine")
        {
        }
};

#endif // POP_MACHINE_H_INCLUDED 


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
28
29
30
31
32
33
34
35
36
37
38
39
40
// machine.h

#ifndef MACHINE_H_INCLUDED
#define MACHINE_H_INCLUDED

#include <string>
#include <vector>

class machine
{
    private:
        // now private and with namespace
        std::string typeName;
        std::string brandName;
        std::vector<std::string> products;

        // implementation now in cpp file
        virtual bool AcquireBrandName();
        virtual bool AcquireProductNames();
        virtual bool AcquireProductCosts();

    public:
        machine(const std::string& typeName_) : typeName(typeName_)
        {
        }

        bool Configure()
        {
            bool ok = AcquireBrandName();
            if(ok)
                ok = AcquireProductNames();
            if(ok)
                ok = AcquireProductCosts();
            return ok;
        }

        void DisplayProducts();
};

#endif // MACHINE_H_INCLUDED 


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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// machine.cpp

#include <iostream>
#include "machine.h"

using namespace std;

/*virtual*/ bool machine::AcquireBrandName()
{
    bool satisfied = false;

    while(!satisfied)
    {
        cout << "Enter the brand name of the " << typeName << endl;
        string newName;
        getline(cin, newName);

        cout << "\nAre you satisfied with this name: " << newName << " ?" << endl;
        cout << "1) Yes" << endl;
        cout << "2) No" << endl;
        int choice = 0;
        cin >> choice;

        if(choice == 1)
        {
            brandName = newName;
            // AcquireProductNames now called from Configure
            satisfied = true;
        }
        else if(choice == 2)
        {
            cin.ignore(250, '\n');
            break;
        }
    }

    return satisfied;
}

/*virtual*/ bool machine::AcquireProductNames()
{
    bool vectEntryLoopEnd = false;

    cout << "Please enter the names of the products " << brandName << " will sell" << endl;
    while(vectEntryLoopEnd != true)
    {
        string enterProducts;
        getline(cin, enterProducts);
        products.push_back(enterProducts);

        // could write a no-case compare utility function (with the help
        // or toupper or tolower)
        if(enterProducts == "Stop" || enterProducts == "stop")
        {
            products.pop_back();
            vectEntryLoopEnd = true;
        }
    }

    return true; // could implement cancel?
}

/*virtual*/ bool machine::AcquireProductCosts()
{
    // TODO
    return true;
}

void machine::DisplayProducts()
{
    for(int i = 0; i < products.size(); i++)
    {
        cout << products[i] << endl;
    }
}

Last edited on
Refactored to split out vending machine from machine

Andy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// main.cpp

#include <iostream>
#include "pop_machine.h"
using namespace std;

int main() {
    machine* pmachine = new pop_machine;
    bool ok = pmachine->Configure();
    if(ok)
        pmachine->Display();
    delete pmachine;

    return 0;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// pop_machine.h

#ifndef POP_MACHINE_H_INCLUDED
#define POP_MACHINE_H_INCLUDED

#include "vending_machine.h"

class pop_machine : public vending_machine
{
    public:
        pop_machine() : vending_machine("pop machine")
        {
        }
};

#endif // POP_MACHINE_H_INCLUDED 


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
28
29
30
// vending_machine.h

#ifndef VENDING_MACHINE_H_INCLUDED
#define VENDING_MACHINE_H_INCLUDED

#include "machine.h"

#include <string>
#include <vector>

class vending_machine : public machine
{
    private:
        // now private and with namespace
        std::vector<std::string> products;

        // implementation now in cpp file
        bool AcquireProductNames();
        bool AcquireProductCosts();

    public:
        vending_machine(const std::string& typeName_) : machine(typeName_)
        {
        }

        virtual bool Configure();
        virtual void Display();
};

#endif // VENDING_MACHINE_H_INCLUDED  


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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// vending_machine.cpp

#include <iostream>
#include "vending_machine.h"

using namespace std;

/*virtual*/ bool vending_machine::Configure()
{
    bool ok = machine::Configure();
    if(ok)
        ok = AcquireProductNames();
    if(ok)
        ok = AcquireProductCosts();
    return ok;
}

/*virtual*/ void vending_machine::Display()
{
    machine::Display();
    cout << endl;
    cout << "products:" << endl;
    for(int i = 0; i < products.size(); i++)
    {
        cout << "  " << products[i] << endl;
    }
}

bool vending_machine::AcquireProductNames()
{
    bool vectEntryLoopEnd = false;

    cout << endl;
    cout << "Please enter the products " << GetBrandName() << " will sell" << endl;
    while(vectEntryLoopEnd != true)
    {
        string enterProducts;
        getline(cin, enterProducts);
        products.push_back(enterProducts);

        // could write a no-case compare utility function (with the help
        // or toupper or tolower)
        if(enterProducts == "Stop" || enterProducts == "stop")
        {
            products.pop_back();
            vectEntryLoopEnd = true;
        }
    }

    return true; // could implement cancel?
}

bool vending_machine::AcquireProductCosts()
{
    // TODO
    return true;
}


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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// machine.h

#ifndef MACHINE_H_INCLUDED
#define MACHINE_H_INCLUDED

#include <string>
#include <vector>

class machine
{
    private:
        // now private and with namespace
        std::string typeName;
        std::string brandName;

        // implementation now in cpp file
        bool AcquireBrandName();

    public:
        machine(const std::string& typeName_) : typeName(typeName_)
        {
        }

        virtual ~machine()
        {
        }

        virtual bool Configure();
        virtual void Display();

        const std::string& GetTypeName() const
        {
            return typeName;
        }

        const std::string& GetBrandName() const
        {
            return brandName;
        }
};

#endif // MACHINE_H_INCLUDED  


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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// machine.cpp

#include <iostream>
#include "machine.h"

using namespace std;

/*virtual*/ bool machine::Configure()
{
    bool ok = AcquireBrandName();
    return ok;
}

/*virtual*/ void machine::Display()
{
    cout << endl;
    cout << "machine:" << endl;
    cout << "  type       : "<< typeName  << endl;
    cout << "  brand name : "<< brandName << endl;
}

bool machine::AcquireBrandName()
{
    bool satisfied = false;

    while(!satisfied)
    {
        cout << "Enter the brand name of the " << typeName << endl;
        string newName;
        getline(cin, newName);

        cout << "\nAre you satisfied with this name: " << newName << " ?" << endl;
        cout << "1) Yes" << endl;
        cout << "2) No" << endl;
        int choice = 0;
        cin >> choice;
        // ignore so getline isn't confused later on
        cin.ignore(250, '\n');

        if(choice == 1)
        {
            brandName = newName;
            // AcquireProductNames now called from Configure
            satisfied = true;
        }
        else if(choice == 2)
        {
            cin.ignore(250, '\n');
            break;
        }
    }

    return satisfied;
}
Last edited on
well a pop machine is a vending machine and its also a type of machine, just like a security camera and a cash register, there both machines, so for them to derive from a machine class makes sense to me. also when should i use virtual functions and when should i use pure virtual functions? so my code has no need for virtual functions yet? also i have a store class what should derive from that? I think the pop machines should since they go into the store.
Last edited on
well a pop machine is a vending machine and its also a type of machine, just like a security camera and a cash register, there both machines, so for them to derive from a machine class makes sense to me.

But what methods do you need at that level? I suppose, maybe a GetAssetTag() method?

when should i use virtual functions

As you no doubt know, you need virtual functions when you want to use polymorphism. In the main the virtual function are base class (here machine) functions that you're exposing to the outside world. That is, the public methods.

But you can also use polymorphism internally when you have a big function which is does what all classes require except for a little bit in the middle. And it is used internally when you use the PIMPL (aka opaque pointer or Cheshire cat) idiom.

also i have a store class what should derive from that? I think the pop machines should since they go into the store.

Remember, inheritence is for is-a relationships. Other relationships are handled though has-a or uses-a relationships (etc. including "is-in-a".)

Of course you could make the machines class members of the store class, though that makes the set of machines fixed until you rebuild your store.

Instead you should get your store to hold pointers to the machines (the direction you were already heading, going by the virtual functions.) Though it might be better to hold cash registers separately to vending machines (are their operations which need to work on one set of machine?), though this depends on the detail of what you want to do, and how you want to do it.

You might even end up with a hierarchy of store classes from the base store class. That depends on whether their are types of store that need special functionality.

Andy

Opaque pointer
http://en.wikipedia.org/wiki/Opaque_pointer
Last edited on
Ok well im just going to make a new project and start fresh here, so if i make a store class what am i going to derive from it, if anything. I think it would be better if i was helped step by step instead of just guessing blindly. I looked around on the internet and this site butthe explanations are just too technical for me so i cant understand them. I created a new class called corner_store. Im not asking anyone to do it for me, thats not what i want. i just want some guidance of making a class the right way and one that works the way it should.

1
2
3
4
5
6
class corner_store
{
    private:
    
    public:
};


ok so in my game which is a store manager you get to choose the different machines you want in your store, only the types like soda machine, slurpie machine, cash register are hard coded, but you can make up the brand name. I have nothing in main and this class is in a .h file, what is my next step.
Last edited on
I think that before you try to assemble the bits you should flesh out what the classes need to do. In particular, how they need to interact (i.e. sketch out a design; an object oriented design!)

So...

- Once the manager has selected the machines for their
  store, do they open up shop and sell to customers?
- If so, do you need to have classes representing the customer,
  the manager, and the staff? If you do then the machines will have
  to interact with them.
- does your shop only sell stuff from machines, or will customers
  be able to pick up produce from shelves and fridges??
- etc


So far, from both your last mail and earlier in the thread, there are the following candidate class:

shop
corner shop

manager
shop assistant?
customer?

produce?

machine
- vending machine
- - soda machine
- - slurpie machine
- cash register


After writing this post I'm no thinking that maybe you should focus on how customers buy things to start with, rather than the machines. They can be added later.

Andy
Last edited on
Ok so basically you start out with an empty shell of a store, then you need to name the store, then you choose the starting machines, (you get 1 free cash register) and you need to choose the stock that you sell. then you can open your store and customers can buy stuff.
Focusing on the customers for the moment: they customer will turn up and look around to see what you've got (e.g. search the shop) or ask one of the shop assistants if you sell something.

Customer: "I'd like a Slurpee please".

Shop assistant: [after searching for the right machine...]

Could say:

"sorry, we don't sell slurpees"
[requirement: can find a machine by name]

or

"sorry, we do sell slurpees, but we're out of frozen water"
[requirement: can query machine status]

but assume it's

"ok, what flavor do you want?"

Customer: - "what flavors do you sell?"

Shop assistant: [after searching for the Slurpee machine for
the favors (producs) it dispenses]
- "Cola, sarsaparilla, orange, lemon, root beer, or yogurt & Mint"
[requirement: can query for all a machines products]

Customer: - "do you have any Mountain Dew flavor?"

Shop assistant: - "sorry, we don't do that flavor"
[requirement: can query a machines for a specific product]
[note: need to know the difference between products you do stock,
but which are currently out of stock and products you don't stock]

Etc


Now, the need to find a machine by name means we need a FindMethod which takes a name (obviously) and have to store the machines in a way that allows you to obtain them by name as well as index (probably.)

(Are you familar with std::map ?? You could either store the Machine in a vector and then search for the machine with the required name. Of use a name to Machine map, which makes finding them easier but means you have to use an iterator when you work on them as a group.)

And your Slurpee machine needs a find product method, but a cash machine doesn't. So you need a bending machine (though maybe dispensing machine might be better? but that's a detail.)

Etc.

The dialog is probably going too far (though some people do go this far), but this is pretty much what you do; it's called use-case analysis (when done formally, unlike here.) It allows you to work out what has to happen to achieve your main aims. Once you've worked out the what, you then look at the how (the implementation.)

So

class Shop
[member variables]
- has zero or more machines
  (accessible by name and index)
[member functions]
- AddMachine
- RemoveMachine (to balance add)
- FindMachine by name
...

Also need a way to deal with all machine?

class Machine
[member variables]
- brand name
[member functions]
- Config
  (prob better than SetBrandName, as you expand?)
- GetBrandName
...


class VendingMachine
[base class]
- class Machine
[member variables]
- has zero or more products
  (accessible by name and index)
[member functions]
- AddProduct
- RemoveProduct
- FindProduct
- GetProductLevel ???
  (this needs working on?)
...


Regarding GetProductLevel. Is it enough to have a method which returns (e.g.) an int to inform you of how many servings are still available? Or do you need a Product class which has a GetLevel method plus other stuff? Etc. This would mean the Machine needs to work with Product instance rather than just products names.

And should the machine now the price of it's products, in the general case? Or should that be for the cash register (in a shop, the bar code identifies the products and the register then looks up the price.)

class SlurpeeMachine
[base class]
class VendingMachine : brand name = "Slurpee Machine"
...


class CashRegister
[base class]
class Machine
[member variables]
- has zero or more product prices
  (accessible by product name and index)
[member functions]
- AddProductPricingInfo
- FindProductPrice
...


Etc

Writing this down, I'm now thinking about how the save-game mechanism will work. Once you've added all the products to the Slurpee machine, you do't nwat to have the store reconfiguring the machine every time it "open". So you need to provide a way for each machine to save its data (serialize it's data to disk) when the game closes, and then read it back in when the game is restarted. While you don't need to worry about the detail of this yet, I would add it to my to do list.

And how real life customers can be a bit random! So need a random number generator somewhere in the game.

I keep a notebook with hand-written notes and sketches to keep track of this kind of thing, which is something you should consider doing if you're already doing so. for small scale projects it's not really worth the effort of writing Word documents or the like.

Andy

PS I was taught to write my notes in pen and never remove or tipex anything out. If you make a mistake, just strike it through and make a note of why you think it's wrong. Then,if it turns out later you were wrong about being wrong, you haven't lost any information.

Sketching out the use cases and classes in your own way is going to be more than enough for your purposes. But if you want to find out more, see the following links fr a starting point. Use-case analysis, User stories, and UML (a set way of drawing class hierachy diagrams, etc.) are all popular with software compaines.

Use-case analysis
http://en.wikipedia.org/wiki/Use-case_analysis

User story
http://en.wikipedia.org/wiki/User_story

Unified Modeling Language
http://en.wikipedia.org/wiki/Unified_Modeling_Language

Last edited on
Ok thank you i will look this all over and keep notes, which is something i never do but ill get in the habit of that. I am just confused on how classes work, the game really isnt important its the classes that im trying to learn. I have watched and read tutorials online and made my own classes but everyone keeps telling me im doing them wrong and i dont get it, classes are not a hard concept, its trying to understand what im doing wrong thats hard. so what does a class thats done right look like?
classes are not a hard concept, ...

In one way, classes are quite simple. The idea for classes and OO came about, as I'm sure you know, as it's supposed to reflect how we interact with the real world. And should make it easier to design software.

But at the same time it can be quite hard to apply it correctly, so your problems are far from unusual. The way to learn about oo is to break down a problem into small steps so it is crystal clear what is going on; once you get the idea you can take short cuts. (This is why I suggested the use-case/user story approach.)

By the way, I think you game idea is both quite good as games go, and also a good game to use to learn about oo programming.

Andy
Last edited on
Pages: 12