Suppose you need to write an inventory control system for automobile parts.
Write a UML class diagram with a base class named AutoPart and some subclasses like, EnginePart, BodyPart, Accessory, etc. and concrete part classes like Alternator, Fender, Radiator, SeatBelt, etc.
Write (but do not implement) class definitions for your classes. What kinds of base class functions will be needed?
How will you guarantee that only the concrete part classes can be instantiated?
The classes in Figure 6.9 are intended to help organize the film collection in the college library.
Implement the Film
classes. Make sure that the constructors have sufficient parameters to initialize all data members. We suggest enum
types FilmTypes
(Action, Comedy, SciFi, ...) and MPAARatings
(G, PG, PG-13, ...) for use in the Entertainment
class.
Implement the FilmList
class as a container of Film
pointers. Make sure that the addFilm()
function does not permit the same Film to be added more than once.
Is it possible to use base class functions such as contains()
or indexOf()
here?
Write client code to test these classes. Put a mixture of Entertainment
and Educational
films into the FilmList
and exercise all the member functions.
Having a container of pointers raises the question of how to destroy it. If you simply "do the right thing" and define a FilmList
destructor that visits each of its pointers and deletes it, you must then worry about client code that contains a function with a FilmList
value parameter. Recall that each value parameter causes a copy of the corresponding argument to be made. That copy is destroyed when the function returns. How should you deal with copying and destroying FilmList
objects?
The FilmList
class enables you to exploit polymorphism but exposes the Film
pointers to client code. So far, this design violates our earlier warning regarding the use of pointers. In general, pointers must be hidden from client code. We discuss an approach to this problem in Section 16.3.
Refine our solution to Extended Example: A Simple Library by adding a LibraryUI
class that handles the interactions with the user as suggested by the following UML diagram.
When you add that class, you should use the client code in Example 6.43 to test your system.
Example 6.43. src/pointer-container/libraryClient-v2.cpp
#include "libraryui.h" #include "library.h" bool saved(false); int main() { Library lib; LibraryUI libui(&lib); while(1) { switch(libui.nextTask()) { case LibraryUI::READ: libui.read(); saved = false; break; case LibraryUI::ADD: libui.enterData(); saved = false; break; case LibraryUI::FIND: libui.find(); break; case LibraryUI::REMOVE: libui.remove(); saved = false; break; case LibraryUI::SAVE: libui.save(); saved = true; break; case LibraryUI::LIST: libui.list(); break; case LibraryUI::QUIT: libui.prepareToQuit(saved); break; default: break; } } }
<include src="src/pointer-container/libraryClient-v2.cpp" href="src/pointer-container/libraryClient-v2.cpp" id="libclientv2" mode="cpp"/>
Example 6.44. solution/library-stdio/library-v2/library.h
#ifndef LIBRARY_H_ #define LIBRARY_H_ #include <QList> #include <QString> #include <QStringList> class RefItem { public: virtual ~RefItem(); QString getItemType() const; QString getISBN() const; QString getTitle() const; int getNumberOfCopies() const; virtual QString toString(QString sep="[::]") const; void setNumberOfCopies(int newVal); protected: RefItem(QString type, QString isbn, QString title, int numCopies=1); RefItem(QStringList& proplist); private: QString m_ItemType, m_ISBN, m_Title; int m_NumberOfCopies; }; class Book : public RefItem { public: Book(QString type, QString isbn, QString title, QString author, QString pub, int year, int numCopies=1); Book(QStringList& proplist); virtual QString toString(QString sep="[::]") const; QString getAuthor() const; QString getPublisher() const; int getCopyrightYear() const; private: QString m_Author, m_Publisher; int m_CopyrightYear; }; class ReferenceBook : public Book { public: enum RefCategory {NONE = -1, Art, Architecture, ComputerScience, Literature, Math, Music, Science}; ReferenceBook(QString type, QString isbn, QString title, QString author, QString pub, int year, RefCategory refcat, int numCopies=1); ReferenceBook(QStringList& proplist); QString toString(QString sep="[::]") const; RefCategory getCategory() const; QString categoryString() const; //returns string version of m_Category static QStringList getRefCategories(); //returns a list of categories private: RefCategory m_Category; }; class TextBook : public Book { public: TextBook(QString type, QString isbn, QString title, QString author, QString pub, int year, QString course, int numCopies=1); TextBook(QStringList& proplist); QString toString(QString sep="[::]") const; QString getCourse() const; private: QString m_Course; }; class Dvd : public RefItem { public: Dvd(QString type, QString isbn, QString title,int disks, bool twoSided, int numCopies=1); Dvd(QStringList& proplist); QString toString(QString sep="[::]") const; int getNumberOfDisks() const; bool isTwoSided() const; private: int m_NumberOfDisks; bool m_TwoSided; }; class Film : public Dvd { public: Film(QString type, QString isbn, QString title,int disks, bool twoSided, QString star,QString director, int minutes, bool blueray, int numCopies=1); Film(QStringList& proplist); QString toString(QString sep="[::]") const; QString getStar() const; QString getDirector() const; int getMinutes() const; bool isBlueray() const; private: QString m_Star, m_Director; int m_Minutes; bool m_BlueRay; }; class DataBase : public Dvd { public: DataBase(QString type, QString isbn, QString title,int disks, bool twoSided, QString protocol, int numCopies=1); DataBase(QStringList& proplist); QString toString(QString sep="[::]") const; QString getDBProtocol() const; private: QString m_DBProtocol; }; class Library : public QList<RefItem*> { public: Library() {} ~Library(); void addRefItem(RefItem* refitem); int removeRefItem(QString isbn); QString toString(QString sep="\n") const; bool isInList(QString isbn); QString getItemString(QString isbn); private: Library(const Library&); Library& operator=(const Library&); RefItem* findRefItem(QString isbn); }; #endif
<include src="solution/library-stdio/library-v2/library.h" href="solution/library-stdio/library-v2/library.h" role="solution" mode="cpp"/>
Example 6.45. solution/library-stdio/library-v2/library.cpp
#include <QStringList> #include <QDebug> #include "library.h" //Note: RefItem constructor is protected. RefItem::RefItem(QString type, QString isbn, QString title, int numCopies) : m_ItemType(type), m_ISBN(isbn), m_Title(title), m_NumberOfCopies(numCopies) { } RefItem::RefItem(QStringList& plst) : m_ItemType(plst.takeFirst()), m_ISBN(plst.takeFirst()), m_Title(plst.takeFirst()), m_NumberOfCopies(plst.takeFirst().toInt()) { } RefItem::~RefItem() { } QString RefItem::getItemType() const { return m_ItemType; } QString RefItem::getISBN() const { return m_ISBN; } QString RefItem::getTitle() const { return m_Title; } int RefItem::getNumberOfCopies() const { return m_NumberOfCopies; } QString RefItem::toString(QString sep) const { return QString("%1%2%3%4%5%6%7").arg(m_ItemType).arg(sep).arg(m_ISBN).arg(sep) .arg(m_Title).arg(sep).arg(m_NumberOfCopies); } void RefItem::setNumberOfCopies(int newValue) { m_NumberOfCopies = newValue; } Book::Book(QString type, QString isbn, QString title, QString author, QString pub, int year, int numCopies) : RefItem(type, isbn, title, numCopies), m_Author(author), m_Publisher(pub), m_CopyrightYear(year) { } Book::Book(QStringList& plst) : RefItem(plst), m_Author(plst.takeFirst()), m_Publisher(plst.takeFirst()), m_CopyrightYear(plst.takeFirst().toInt()) { } QString Book::toString(QString sep) const { return QString("%1%2%3%4%5%6%7").arg(RefItem::toString(sep)).arg(sep) .arg(m_Author).arg(sep).arg(m_Publisher).arg(sep) .arg(m_CopyrightYear); } QString Book::getAuthor() const { return m_Author; } QString Book::getPublisher() const { return m_Publisher; } int Book::getCopyrightYear() const { return m_CopyrightYear; } ReferenceBook::ReferenceBook(QString type, QString isbn, QString title, QString author, QString pub, int year, RefCategory refcat, int numCopies) : Book(type, isbn, title, author,pub,year,numCopies), m_Category(refcat) { } ReferenceBook::ReferenceBook(QStringList& plst) : Book(plst), m_Category(static_cast<RefCategory>(plst.takeFirst().toInt())) { } QString ReferenceBook::toString(QString sep) const { return QString("%1%2%3").arg(Book::toString(sep)).arg(sep) .arg(categoryString()); } ReferenceBook::RefCategory ReferenceBook::getCategory() const { return m_Category; } QString ReferenceBook::categoryString() const { switch(m_Category) { case Art: return "Art"; case Architecture: return "Architecture"; case ComputerScience: return "ComputerScience"; case Literature: return "Literature"; case Math: return "Math"; case Music: return "Music"; case Science: return "Science"; default: return "None"; } } QStringList ReferenceBook::getRefCategories() { QStringList cats; cats << "Art" << "Architecture" << "ComputerScience" << "Literature" << "Math" << "Music" << "Science"; return cats; } TextBook::TextBook(QString type, QString isbn, QString title, QString author, QString pub, int year, QString course, int numCopies) : Book(type, isbn, title, author,pub,year,numCopies), m_Course(course) { } TextBook::TextBook(QStringList& plst) : Book(plst), m_Course(plst.takeFirst()) { } QString TextBook::toString(QString sep) const { return QString("%1%2%3").arg(Book::toString(sep)).arg(sep).arg(m_Course); } QString TextBook::getCourse() const { return m_Course; } Dvd::Dvd(QString type, QString isbn, QString title, int disks, bool twoSided, int numCopies) : RefItem(type, isbn, title, numCopies), m_NumberOfDisks(disks), m_TwoSided(twoSided) { } Dvd::Dvd(QStringList& plst) : RefItem(plst), m_NumberOfDisks(plst.takeFirst().toInt()), m_TwoSided(plst.takeFirst().toInt()) { } QString Dvd::toString(QString sep) const { return QString("%1%2%3%4%5").arg(RefItem::toString(sep)).arg(sep) .arg(m_NumberOfDisks).arg(sep).arg(m_TwoSided); } int Dvd::getNumberOfDisks() const { return m_NumberOfDisks; } bool Dvd::isTwoSided() const { return m_TwoSided; } Film::Film(QString type, QString isbn, QString title,int disks, bool twoSided, QString star, QString director, int minutes, bool blueray, int numCopies) : Dvd(type, isbn, title, disks,twoSided,numCopies), m_Star(star), m_Director(director), m_Minutes(minutes), m_BlueRay(blueray) { } Film::Film(QStringList& plst) : Dvd(plst), m_Star(plst.takeFirst()), m_Director(plst.takeFirst()), m_Minutes(plst.takeFirst().toInt()), m_BlueRay(plst.takeFirst().toInt()) { } QString Film::toString(QString sep) const { return QString("%1%2%3%4%5%6%7%8%9").arg(Dvd::toString(sep)).arg(sep) .arg(m_Star).arg(sep).arg(m_Director).arg(sep) .arg(m_Minutes).arg(sep).arg(m_BlueRay); } QString Film::getStar() const { return m_Star; } QString Film::getDirector() const { return m_Director; } int Film::getMinutes() const { return m_Minutes; } bool Film::isBlueray() const { return m_BlueRay; } DataBase::DataBase(QString type, QString isbn, QString title, int disks, bool twoSided, QString protocol, int numCopies) : Dvd(type, isbn, title, disks,twoSided,numCopies), m_DBProtocol(protocol) { } DataBase::DataBase(QStringList& plst) : Dvd(plst), m_DBProtocol(plst.takeFirst()) { } QString DataBase::toString(QString sep) const { return QString("%1%2%3").arg(Dvd::toString(sep)).arg(sep).arg(m_DBProtocol); } QString DataBase::getDBProtocol() const { return m_DBProtocol; } Library::~Library() { qDeleteAll(*this); clear(); } Library::Library(const Library&) : QList<RefItem*>() {} Library& Library::operator=(const Library&) { return *this; } void Library::addRefItem(RefItem* refitem) { QString isbn(refitem->getISBN()); RefItem* oldItem(findRefItem(isbn)); if(oldItem==0) append(refitem); else { qDebug() << isbn << " Already in list:\n" << oldItem->toString() << "\nIncreasing number of copies " << "and deleting new pointer." ; int newNum(oldItem->getNumberOfCopies() + refitem->getNumberOfCopies()); oldItem->setNumberOfCopies(newNum); delete refitem; refitem = 0; } } int Library::removeRefItem(QString isbn) { RefItem* ref(findRefItem(isbn)); int numCopies(-1); if(ref) { numCopies = ref->getNumberOfCopies() - 1; if(numCopies== 0) { removeAll(ref); delete ref; } else ref->setNumberOfCopies(numCopies); } return numCopies; } RefItem* Library::findRefItem(QString isbn) { for(int i = 0; i < size(); ++i) { if(at(i)->getISBN().trimmed() == isbn.trimmed()) return at(i); } return 0; } bool Library::isInList(QString isbn) { return findRefItem(isbn); } QString Library::toString(QString sep) const { QStringList reflst; for(int i = 0; i < size(); ++i) reflst << at(i)->toString(); return reflst.join(sep); } QString Library::getItemString(QString isbn) { RefItem* ref(findRefItem(isbn)); if(ref) return ref->toString(); else return QString(); }
<include src="solution/library-stdio/library-v2/library.cpp" href="solution/library-stdio/library-v2/library.cpp" role="solution" mode="cpp"/>
Refine your solution to 3 so that each class has its own corresponding UI class, as suggested by the following UML diagram.
Example 6.46. solution/library-stdio/library-v3/library.h
[ . . . . ] class RefItem { public: virtual ~RefItem(); QString getItemType() const; QString getISBN() const; QString getTitle() const; int getNumberOfCopies() const; virtual QString toString(QString sep="[::]") const; void setNumberOfCopies(int newVal); protected: RefItem(QString type, QString isbn, QString title, int numCopies=1); RefItem(QStringList& proplist); private: QString m_ItemType, m_ISBN, m_Title; int m_NumberOfCopies; }; class Book : public RefItem { public: Book(QString type, QString isbn, QString title, QString author, QString pub, int year, int numCopies=1); Book(QStringList& proplist); virtual QString toString(QString sep="[::]") const; QString getAuthor() const; QString getPublisher() const; int getCopyrightYear() const; private: QString m_Author, m_Publisher; int m_CopyrightYear; }; class ReferenceBook : public Book { public: enum RefCategory {NONE = -1, Art, Architecture, ComputerScience, Literature, Math, Music, Science}; ReferenceBook(QString type, QString isbn, QString title, QString author, QString pub, int year, RefCategory refcat, int numCopies=1); ReferenceBook(QStringList& proplist); QString toString(QString sep="[::]") const; RefCategory getCategory() const; QString categoryString() const; //returns string version of m_Category static QStringList getRefCategories(); //returns a list of categories private: RefCategory m_Category; }; [ . . . . ]
<include src="solution/library-stdio/library-v3/library.h" href="solution/library-stdio/library-v3/library.h" role="solution" mode="cpp"/>
Example 6.47. solution/library-stdio/library-v3/library.cpp
#include <QStringList> #include <QDebug> #include "library.h" //Note: RefItem constructor is protected. RefItem::RefItem(QString type, QString isbn, QString title, int numCopies) : m_ItemType(type), m_ISBN(isbn), m_Title(title), m_NumberOfCopies(numCopies) { } RefItem::RefItem(QStringList& plst) : m_ItemType(plst.takeFirst()), m_ISBN(plst.takeFirst()), m_Title(plst.takeFirst()), m_NumberOfCopies(plst.takeFirst().toInt()) { } RefItem::~RefItem() { } QString RefItem::getItemType() const { return m_ItemType; } QString RefItem::getISBN() const { return m_ISBN; } QString RefItem::getTitle() const { return m_Title; } int RefItem::getNumberOfCopies() const { return m_NumberOfCopies; } QString RefItem::toString(QString sep) const { return QString("%1%2%3%4%5%6%7").arg(m_ItemType).arg(sep).arg(m_ISBN).arg(sep) .arg(m_Title).arg(sep).arg(m_NumberOfCopies); } void RefItem::setNumberOfCopies(int newValue) { m_NumberOfCopies = newValue; } Book::Book(QString type, QString isbn, QString title, QString author, QString pub, int year, int numCopies) : RefItem(type, isbn, title, numCopies), m_Author(author), m_Publisher(pub), m_CopyrightYear(year) { } Book::Book(QStringList& plst) : RefItem(plst), m_Author(plst.takeFirst()), m_Publisher(plst.takeFirst()), m_CopyrightYear(plst.takeFirst().toInt()) { } QString Book::toString(QString sep) const { return QString("%1%2%3%4%5%6%7").arg(RefItem::toString(sep)).arg(sep) .arg(m_Author).arg(sep).arg(m_Publisher).arg(sep) .arg(m_CopyrightYear); } QString Book::getAuthor() const { return m_Author; } QString Book::getPublisher() const { return m_Publisher; } int Book::getCopyrightYear() const { return m_CopyrightYear; } ReferenceBook::ReferenceBook(QString type, QString isbn, QString title, QString author, QString pub, int year, RefCategory refcat, int numCopies) : Book(type, isbn, title, author,pub,year,numCopies), m_Category(refcat) { } ReferenceBook::ReferenceBook(QStringList& plst) : Book(plst), m_Category(static_cast<RefCategory>(plst.takeFirst().toInt())) { } QString ReferenceBook::toString(QString sep) const { return QString("%1%2%3").arg(Book::toString(sep)).arg(sep) .arg(categoryString()); } ReferenceBook::RefCategory ReferenceBook::getCategory() const { return m_Category; } QString ReferenceBook::categoryString() const { switch(m_Category) { case Art: return "Art"; case Architecture: return "Architecture"; case ComputerScience: return "ComputerScience"; case Literature: return "Literature"; case Math: return "Math"; case Music: return "Music"; case Science: return "Science"; default: return "None"; } } QStringList ReferenceBook::getRefCategories() { QStringList cats; cats << "Art" << "Architecture" << "ComputerScience" << "Literature" << "Math" << "Music" << "Science"; return cats; } TextBook::TextBook(QString type, QString isbn, QString title, QString author, QString pub, int year, QString course, int numCopies) : Book(type, isbn, title, author,pub,year,numCopies), m_Course(course) { } TextBook::TextBook(QStringList& plst) : Book(plst), m_Course(plst.takeFirst()) { } QString TextBook::toString(QString sep) const { return QString("%1%2%3").arg(Book::toString(sep)).arg(sep).arg(m_Course); } QString TextBook::getCourse() const { return m_Course; } Dvd::Dvd(QString type, QString isbn, QString title, int disks, bool twoSided, int numCopies) : RefItem(type, isbn, title, numCopies), m_NumberOfDisks(disks), m_TwoSided(twoSided) { } Dvd::Dvd(QStringList& plst) : RefItem(plst), m_NumberOfDisks(plst.takeFirst().toInt()), m_TwoSided(plst.takeFirst().toInt()) { } QString Dvd::toString(QString sep) const { return QString("%1%2%3%4%5").arg(RefItem::toString(sep)).arg(sep) .arg(m_NumberOfDisks).arg(sep).arg(m_TwoSided); } int Dvd::getNumberOfDisks() const { return m_NumberOfDisks; } bool Dvd::isTwoSided() const { return m_TwoSided; } Film::Film(QString type, QString isbn, QString title,int disks, bool twoSided, QString star, QString director, int minutes, bool blueray, int numCopies) : Dvd(type, isbn, title, disks,twoSided,numCopies), m_Star(star), m_Director(director), m_Minutes(minutes), m_BlueRay(blueray) { } Film::Film(QStringList& plst) : Dvd(plst), m_Star(plst.takeFirst()), m_Director(plst.takeFirst()), m_Minutes(plst.takeFirst().toInt()), m_BlueRay(plst.takeFirst().toInt()) { } QString Film::toString(QString sep) const { return QString("%1%2%3%4%5%6%7%8%9").arg(Dvd::toString(sep)).arg(sep) .arg(m_Star).arg(sep).arg(m_Director).arg(sep) .arg(m_Minutes).arg(sep).arg(m_BlueRay); } QString Film::getStar() const { return m_Star; } QString Film::getDirector() const { return m_Director; } int Film::getMinutes() const { return m_Minutes; } bool Film::isBlueray() const { return m_BlueRay; } DataBase::DataBase(QString type, QString isbn, QString title, int disks, bool twoSided, QString protocol, int numCopies) : Dvd(type, isbn, title, disks,twoSided,numCopies), m_DBProtocol(protocol) { } DataBase::DataBase(QStringList& plst) : Dvd(plst), m_DBProtocol(plst.takeFirst()) { } QString DataBase::toString(QString sep) const { return QString("%1%2%3").arg(Dvd::toString(sep)).arg(sep).arg(m_DBProtocol); } QString DataBase::getDBProtocol() const { return m_DBProtocol; } Library::~Library() { qDeleteAll(*this); clear(); } Library::Library(const Library&) : QList<RefItem*>() {} Library& Library::operator=(const Library&) { return *this; } void Library::addRefItem(RefItem* refitem) { QString isbn(refitem->getISBN()); RefItem* oldItem(findRefItem(isbn)); if(oldItem==0) append(refitem); else { qDebug() << isbn << " Already in list:\n" << oldItem->toString() << "\nIncreasing number of copies " << "and deleting new pointer." ; int newNum(oldItem->getNumberOfCopies() + refitem->getNumberOfCopies()); oldItem->setNumberOfCopies(newNum); delete refitem; refitem = 0; } } int Library::removeRefItem(QString isbn) { RefItem* ref(findRefItem(isbn)); int numCopies(-1); if(ref) { numCopies = ref->getNumberOfCopies(); if(numCopies== 0) { removeAll(ref); delete ref; } else ref->setNumberOfCopies(numCopies); } return numCopies; } RefItem* Library::findRefItem(QString isbn) { for(int i = 0; i < size(); ++i) { if(at(i)->getISBN().trimmed() == isbn.trimmed()) return at(i); } return 0; } bool Library::isInList(QString isbn) { return findRefItem(isbn); } QString Library::toString(QString sep) const { QStringList reflst; for(int i = 0; i < size(); ++i) reflst << at(i)->toString(); return reflst.join(sep); } QString Library::getItemString(QString isbn) { RefItem* ref(findRefItem(isbn)); if(ref) return ref->toString(); else return QString(); }
<include src="solution/library-stdio/library-v3/library.cpp" href="solution/library-stdio/library-v3/library.cpp" role="solution" mode="cpp"/>
Change the implementation of the Library
class so that it is derived from QMap<QString, RefItem*> where the QString key is the ISBN code.
[ fromfile: inheritance-intro.xml id: ex-inheritance ]
Generated: 2012-03-02 | © 2012 Alan Ezust and Paul Ezust. |