This is a toy inventory database program which demonstrates a use
of inheritance. There is a base class DBItem which represents
any database item. Each type of item is derived from this base.
The database itself is simply a map, where the data type is
a pointer to the base class, so it can contain contain any of these
objects. Inheritance only works with pointers; if you tried to
create the map as
std::map<int, DBItem> db;
the program won't work. The program could be written with the map
std::map<int, DBItem *> db;
and some other changes, including having to go through and delete
all the objects when done. Using the smart pointer saves this work.
This program also builds an index using a std::multimap,
which is similar to a map, but allows a key to be associated with
multiple values.
Notice how the derived class constructors send their parameters to
their base classes in an initializer list between the constructor
parameter list and the body. These lists are also used here to initialize
data fields. Often, these could be accomplished with assignments in the
constructor body, but sometimes not. For instance, data fields marked
const can only be initialized in the constructor list.
Also, fields which are class objects and need constructor parameters
must be initialized here.
Methods which replace ones in the base class are declared
override. This is not required, but if override is indended,
the declaration documents it and avoids mistakes. If the method
does not actually override one in the base class, it becomes
an errror.
/*
* This demonstrates a toy (in-memory) database to show a use of inheritance.
*/
#include <cctype>
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <utility>
#include <memory>
#include <map>
using std::string;
using std::make_shared;
/*
* All database entries. Base class. This class is not technically abstract,
* since it contains no abstract functions. But think of it that way. There's
* not likely a good reason to instantiate one of these except as part of a
* derived class object.
*/
class DBItem {
public:
// Construct a new item.
DBItem(int code, int qty = 0): m_code(code), m_qty(qty) { }
// Obvious extractors and manipulators.
int code() const { return m_code; }
int qty() const { return m_qty; }
void set_qty(int qty) { m_qty = qty; }
void inc_qty(int qty) { m_qty += qty; }
// Return the item description. Can be replaced.
virtual string descr() const {
return "Item " + std::to_string(m_code);
}
protected:
const int m_code; // Inventory code number.
int m_qty; // Item quantity.
};
/*
* Coffee maker. Derived from the database object.
*/
class CoffeeMaker: public DBItem {
public:
// Create a coffeemaker, with the given inventory code, size (in
// cups) and inventory quantity.
CoffeeMaker(int code, int ncup, int qty = 0):
DBItem(code, qty), m_ncup(ncup) { }
// Description. This Is Virtual Regardless of the keyword, since that's
// inherited, but useful as documentation.
virtual string descr() const override {
return "Coffee maker, " + std::to_string(m_ncup) + " cups";
}
protected:
// Size of the coffee maker.
const int m_ncup;
};
/*
* Monitor.
*/
class Monitor: public DBItem {
public:
// Create a monitor with the given inventory code, make, size and
// quantity on hand.
Monitor(int code, string make, int wdpx, int htpx, int qty = 0):
DBItem(code, qty), m_make(make), m_wdpx(wdpx), m_htpx(htpx) { }
// Description.
virtual string descr() const override {
return m_make + " computer monitor " +
std::to_string(m_wdpx) + "x" + std::to_string(m_htpx);
}
protected:
// Width and height of the monitor.
const int m_wdpx, m_htpx;
// Maker.
const string m_make;
};
/*
* CD disk
*/
class CD: public DBItem {
public:
// Create a CD object with the given inventory code, artist and
// title, and quantity on hand.
CD(int code, const string &artist, const string &title, int qty = 0):
DBItem(code, qty), m_artist(artist), m_title(title) { }
// Description.
virtual string descr() const override {
return m_artist + ": " + m_title + " (CD)";
}
protected:
// CD artist and title.
const string m_artist, m_title;
};
/*
* There should be a standard library utility for this.
*/
string downcase(string str)
{
for(char &c: str) { c = tolower(c); }
return str;
}
/*
* Trim punctuation.
*/
string trim(string str)
{
while(str.size() > 0 && !isalnum(str[0])) { str.erase(0,1); }
while(str.size() > 0 && !isalnum(str.end()[-1])) { str.erase(str.end()-1); }
return str;
}
/*
* Place a few items into our inventory then support searches.
*/
int main(int argc, char **argv)
{
// This will serv as our toy inventory database. The data items must be
// pointers to the base class. Collections are homogeneous, so
// we can't put different types of things into one, but we can
// make a collection of the base class pointers, and have some of
// them point to derived objects.
std::map<int, std::shared_ptr<DBItem>> db;
// Put some random items into the database. When you put all the items in one list
// it won't compile because their types differ. I think this is fixable, but three loops
// works fine.
for(auto &i: {
make_shared<CoffeeMaker>(1872, 12, 10),
make_shared<CoffeeMaker>(1880, 12, 8),
make_shared<CoffeeMaker>(1810, 8, 3),
make_shared<CoffeeMaker>(1110, 8, 10) }) {
db[i->code()] = i;
}
for(auto &i: {
make_shared<Monitor>(4715, "HP", 1600, 900, 0),
make_shared<Monitor>(4499, "HP", 1920, 1200, 10),
make_shared<Monitor>(4412, "Dell", 1920, 1200, 10),
make_shared<Monitor>(4711, "Dell", 800, 600, 47),
make_shared<Monitor>(4021, "HP", 1920, 1080, 10),
make_shared<Monitor>(4631, "Samsung", 1920, 1080, 10) }) {
db[i->code()] = i;
}
for(auto &i: {
make_shared<CD>(3812, "Pink Floyd",
"Dark Side of the Moon", 5),
make_shared<CD>(3216, "Mannheim Steamroller",
"Fresh Aire V", 21),
make_shared<CD>(3012, "Nick Drake",
"Five Leaves Left", 10),
make_shared<CD>(3887, "October Project",
"Falling Farther In", 8),
make_shared<CD>(3781, "Dar Williams",
"The Beauty of the Rain", 0),
make_shared<CD>(3731, "The Left Banke",
"There's Gonna Be A Storm", 18) }) {
db[i->code()] = i;
}
// Build a word index. This uses a multimap, which allows multiple
// entries for a key. The keys will be words from the descriptions.
std::multimap<string, int> index;
for(auto &pair: db) { // Each pair is a key, value from the map.
std::istringstream in(pair.second->descr());
string word;
while(in >> word) {
word = trim(downcase(word));
if(word.size() > 1 && word != "of" && word != "the")
index.insert(std::make_pair(word, pair.first));
}
}
// Run searches.
std::cout << "Find: ";
string word;
while(std::cin >> word) {
// If you forgot ^d.
if(word == "quit") { exit(0); }
// The function returns a range of matches. May be more than one in a
// multimap
auto range = index.equal_range(trim(downcase(word)));
if(range.first == range.second) {
std::cout << word << " not found" << std::endl;
} else {
// Scan the range of matches. Each are in the
// index, where the second item is the inventory
// number used to look up the item record.
for(auto i = range.first; i != range.second; ++i) {
// Get the database item from the stock number in the index.
auto itm = db.find(i->second)->second;
// Print a description.
std::cout << itm->code() << " " << itm->descr()
<< std::endl;
if(itm->qty() == 0) {
std::cout << " Out of stock";
} else {
std::cout << " " << itm->qty()
<< " in stock";
}
std::cout << std::endl;
}
}
std::cout << "Find: ";
}
}