simple and extendable shop My task is to write a simple shop. The requirements are : 


It can have multiple items
Each item its own price and quantity
Each customer basic identifier such as full name
Each customer receives clear receipt
Shop keeps the shopping history 


It's meant to be flexible, easy to expand, high performance considered. I'd be grateful if someone could review it and point out what can/should be improved or changed. Thanks!

#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;set&gt;
#include &lt;map&gt;
#include &lt;list&gt;

using namespace std;

class receipt {
private:
    string prodName;
    int quant;

public:
    receipt(const string&amp; prdNm, int qnt)
          : prodName(prdNm), quant(qnt)
    {}
    string getProdName() const { return prodName; }
    int    getQuant() const { return quant; }

};

class client {
private:
    string name;
    vector&lt;receipt&gt; receipts;

public:
    client(const string&amp; n): name(n) {}
    string getName() const { return name; }

    void takeReceipt (const string&amp; prodName, int quant){
        receipts.push_back( receipt(prodName, quant) );
    }

    void printReceipts(int num) const {
        // printing receipts
    }

    bool operator&lt; (const client&amp; c) const {
        return name &lt; c.name;
    }
};

class product {
private:
    string name;
    int price;

public:
    product(const string&amp; n, int p): name(n), price(p) {}
    string getName() const { return name; }
    int getPrice() const { return price; }

    bool operator&lt; (const product&amp; p) const {
        return name &lt; p.name;
    }
};

class record {
private:
    string clientName;
    string prodName;
    int quant;

public:
    record(const string&amp; clNm, const string&amp; prdNm, int qnt)
          : clientName(clNm), prodName(prdNm), quant(qnt)
    {}
    string getClientName() const { return clientName; }
    string getProdName() const { return prodName; }
    int    getQuant() const { return quant; }

};

class shop {
private:
    set&lt;client&gt; clients;
    map&lt;product, int&gt; products;
    list&lt;record&gt; records;

public:
    void addClient(const client&amp; cli) {
        clients.insert(cli);
    }

    void removeClient(const client&amp; cli) {
        clients.erase(cli);
    }

    void addProduct(const product&amp; pr, int quant) {
        products[pr] += quant;
    }

    void removeProduct(const product&amp; pr, int quant) {

        if (products[pr] &lt;= quant)
            products[pr] = 0;
        else
            products[pr] -= quant;
    }

    void doTransaction(client&amp; cli, const product&amp; prod, int quant) {

        if ( clients.find(cli.getName()) == clients.end() )
        {
            // client is not on the list of valid clients.
            return;
        }

        if (products[prod] &gt;= quant)
        {
            // client is buying
            products[prod] -= quant;
            records.push_back( record(cli.getName(), prod.getName(), quant) );
            cli.takeReceipt(prod.getName(), quant);
        }
        else
        {
            // product not available in the quantity
        }
    }

    void printClients() const {
        // printing all the clients
    }

    void printRecords(int num) const {
        // Printing last records of previous transactions
    }

    void printProducts() const {
        // Printing all the available products
    }
};

int main(){

    shop s;
    client cm("Mike"), cj("John"), ca("Andy");
    product pot("potatoe", 5), app("apple", 7), nut("nut", 3);

    s.addClient(cm);
    s.addClient(cj);
    s.addClient(ca);
    s.addProduct(pot, 100);
    s.addProduct(app, 50);
    s.addProduct(nut, 20);

    s.removeClient(ca);
    s.removeProduct(pot, 20);
    s.removeProduct(nut, 20);

    s.doTransaction(cm, pot, 30);
    s.doTransaction(cj, app, 10);
    s.doTransaction(cm, nut, 5);
    s.doTransaction(cm, app, 5);

    return 0;
}
Don't do using namespace std;

Qualify names properly with std:: where necessary.



string getProdName() const { return prodName; }
int    getQuant() const { return quant; }


Avoid abbreviations as they harm code clarity. Most IDE's provide auto-completion, so abbreviations don't save typing anyway.



receipt(const string&amp; prdNm, int qnt)
      : prodName(prdNm), quant(qnt)
{}


The initializer list allows you to use the same name for the function argument as for the member variable, so this would be fine:

receipt(std::string productName, int quantity)
      : productName(std::move(productName)), quantity(quantity)
{}


(Since the product name is a "sink argument" (we want to copy and store it locally), we can take it by value and move it into place).



I don't think it's a good idea to overload operator&lt; for these classes. These aren't mathematical types, so there's no inherent ordered relationship. It would make just as much sense to sort by product price as by name.

We're using the operator&lt; for sorting in a particular structure, so we want to associate the ordering with that structure, not with the type itself. We can do that using a "functor" class to define comparison, and specifying it as a template argument:

struct ProductLessThanByName
{
    bool operator()(const Product&amp; a, const Product&amp; b) const {
        return a.getName() &lt; b.getName();
    }
};

...

    std::map&lt;Product, int, ProductLessThanByName&gt; products;




record is a rather ambiguous name. Maybe client_purchases or purchase_record or something might be better.

Since we already have the receipt class, that stores product and quantity, we could perhaps use that internally.



Although encapsulation is important, people often interpret it as "make all member variables private and add getter / setter functions". This is fine for classes that have associated logic in member functions, or where we can't change a member without extra work (we must adhere to the class invariants).

However, it's unnecessary for simple data containers. If we don't want to allow the user to change a member of the product or receipt classes, we can enforce that by giving them a const&amp; to the class. So we can save a lot of work and use plain structs. e.g.:

struct  receipt
{
    std::string productName;
    int quantity;
};




It's probably better to use unsigned types for the quantity and price. We don't expect these to ever be negative, and it's easier to rule out invalid values.



As far as the design goes, I'm not sure we need to add clients outside of a transaction. I don't think it really makes sense to "remove" a client either. We have to keep the transaction history intact.

I think it would be helpful to separate the idea of "adding a product-type to the database" from "adding stock (actual items of a product-type) to the shop".

As Kyy13 notes in the comments, we need to store the price at which we sold an item in the transaction history.