6.9. Containers of Pointers

[ fromfile: inheritance-intro.xml id: containersofpointers ]

Copies, Copies, Copies

Extended Example: A Simple Library. 

Figure 6.6. Reference Library UML Diagram

Reference Library UML Diagram

Example 6.30. src/pointer-container/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;
};

Example 6.31. src/pointer-container/library.h

[ . . . . ]

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;         1
   static QStringList getRefCategories();  2
private:
   RefCategory m_Category;
};

1

Returns string version of m_Category.

2

Returns a list of categories.


Example 6.32. src/pointer-container/library.cpp

[ . . . . ]

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);
}
[ . . . . ]

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 ReferenceBook::toString(QString sep) const {
   return QString("%1%2%3").arg(Book::toString(sep)).arg(sep)
                          .arg(categoryString());
}
[ . . . . ]

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";
   }
}

Example 6.33. src/pointer-container/library.cpp

[ . . . . ]

RefItem::RefItem(QStringList& plst) : m_ItemType(plst.takeFirst()), 
         m_ISBN(plst.takeFirst()), m_Title(plst.takeFirst()),
         m_NumberOfCopies(plst.takeFirst().toInt()) 
{ }
[ . . . . ]

Book::Book(QStringList& plst) : RefItem(plst), 
      m_Author(plst.takeFirst()), m_Publisher(plst.takeFirst()),
      m_CopyrightYear(plst.takeFirst().toInt())
{ }
[ . . . . ]

ReferenceBook::ReferenceBook(QStringList& plst) : Book(plst), 
     m_Category(static_cast<RefCategory>(plst.takeFirst().toInt()))
{ }

Example 6.34. src/pointer-container/library.h

[ . . . . ]

class Library : public QList<RefItem*> {
public:
   Library() {}
   ~Library();                             1
   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);
};

1

A container of pointers must have a destructor!


Example 6.35. src/pointer-container/library.cpp

[ . . . . ]

Library::~Library() {
   qDeleteAll(*this);
   clear();
}

Library::Library(const Library&) : QList<RefItem*>() {}

Library& Library::operator=(const Library&) {
   return *this;
}

void Library::addRefItem(RefItem*& refitem) { 1
   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;                         2
      refitem = 0;                            3 
   }
}

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;
}

1

Parameter is a pointer reference so that null assignment after delete is possible.

2

Not in a managed container.

3

Reference parameter!


Example 6.36. src/pointer-container/library.cpp

[ . . . . ]


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();
}

[Note] Note

We did not have the option to use the foreach() macro in the implementations of Library::findRefItem() and Library::toString() because the foreach() macro needs to make a copy of the container that it traverses. Because the copy constructor is private, that is not possible. Keep this in mind when you work with QObjects in later chapters.

Example 6.37. src/pointer-container/libraryClient.cpp

[ . . . . ]

QTextStream cout(stdout);
QTextStream cin(stdin);
enum Choices {READ=1, ADD, FIND, REMOVE, SAVE, LIST, QUIT};
enum Types {BOOK, REFERENCEBOOK, TEXTBOOK, DVD, FILM, DATADVD};
const QStringList TYPES = (QStringList() << "BOOK" << "REFERENCEBOOK"
   << "TEXTBOOK" << "DVD" << "FILM" << "DATADVD");
bool saved(false);

Example 6.38. src/pointer-container/libraryClient.cpp

[ . . . . ]

Choices nextTask() {
   int choice;
   QString response;
   do {
      cout << READ << ". Read data from a file.\n"
           << ADD << ". Add items to the Library.\n"
           << FIND << ". Find and display an item.\n"
           << REMOVE << ". Remove an item from the Library.\n"
           << SAVE << ". Save the Library list to a file.\n"
           << LIST << ". Brief listing of Library items.\n"
           << QUIT << ". Exit from this program.\n"
           << "Your choice: " << flush;
     response = cin.readLine();
     choice = response.toInt();
   } while(choice < READ or choice > QUIT);
   return static_cast<Choices>(choice);
}

void add(Library& lib, QStringList objdata) {
   cout << objdata.join("[::]") << endl;
   QString type = objdata.first();
   RefItem* ref;
   switch(static_cast<Types>(TYPES.indexOf(type))) {
   case BOOK: 
      ref = new Book(objdata);
      lib.addRefItem(ref);
      break;
   case REFERENCEBOOK: 
      ref = new ReferenceBook(objdata);
      lib.addRefItem(ref);
         break;
[ . . . . ]

   default: qDebug() << "Bad type in add() function";
   }
}

Example 6.39. src/pointer-container/libraryClient.cpp

[ . . . . ]

void save(Library& lib) {
   QFile outf("libfile");
   outf.open(QIODevice::WriteOnly);
   QTextStream outstr(&outf);
   outstr << lib.toString();
   outf.close();
}

Example 6.40. src/pointer-container/libraryClient.cpp

[ . . . . ]

void read(Library& lib) {
   const QString sep("[::]");
   const int BADLIMIT(5); //max number of bad lines
   QString line, type;
   QStringList objdata;
   QFile inf("libfile");
   inf.open(QIODevice::ReadOnly);
   QTextStream instr(&inf);
   int badlines(0);
   while(not instr.atEnd()) {
      if(badlines >= BADLIMIT) {
         qDebug() << "Too many bad lines! Aborting.";
         return;
      }
      line = instr.readLine();
      objdata = line.split(sep);
      if(objdata.isEmpty()) {
         qDebug() << "Empty Line in file!";
         ++badlines;
      }
      else if(not TYPES.contains(objdata.first())) {
         qDebug() << "Bad type in line: " << objdata.join(";;;");
         ++badlines;
      }
      else
         add(lib, objdata);
    }
}

Example 6.41. src/pointer-container/libraryClient.cpp

[ . . . . ]

QStringList promptRefItem() {
   const int MAXCOPIES(10);
   const int ISBNLEN(13);
   int copies;
   QString str;
   QStringList retval;
   while(1) {
      cout << "ISBN ("<< ISBNLEN << " digits): " << flush;
      str = cin.readLine();
      if(str.length() == ISBNLEN) {
         retval << str;
         break;
      }
   }
   cout << "Title: " << flush;
   retval << cin.readLine();
   while(1) {
      cout << "Number of copies: " << flush;
      copies = cin.readLine().toInt();
      if(copies > 0 and copies <= MAXCOPIES) {
         str.setNum(copies);
         break;
      }
   }
   retval << str;
   return retval;
}

QStringList promptBook() {
   static const int MINYEAR(1900), 
                    MAXYEAR(QDate::currentDate().year());
   int year;
   QStringList retval(promptRefItem());
   QString str;
   cout << "Author: " << flush;
   retval << cin.readLine();
   cout << "Publisher: " << flush;
   retval << cin.readLine();
   while(1) {
      cout << "Copyright year: " << flush;
      year = cin.readLine().toInt();
      if(year >= MINYEAR and year <= MAXYEAR) {
         str.setNum(year);
         break;
      }
   }
   retval << str;
   return retval;
}

QStringList promptReferenceBook() {
   int idx(0);
   bool ok;
   QString str;
   QStringList retval(promptBook());
   QStringList cats(ReferenceBook::getRefCategories());
   while(1) {
      cout << "Enter the index of the correct Reference Category: ";
      for(int i = 0; i < cats.size(); ++i)
         cout << "\n\t(" << i << ") " << cats.at(i);
      cout << "\n\t(-1)None of these\t:::" << flush;
      idx = cin.readLine().toInt(&ok);
      if(ok) {
         retval << str.setNum(idx);
         break;
      }
   }
   return retval;
}
[ . . . . ]

void enterData(Library& lib) {
   QString typestr;
   while(1) {
      cout << "Library item type: " << flush;
      typestr = cin.readLine();
      if(not TYPES.contains(typestr)) {
         cout << "Please enter one of the following types:\n"
              << TYPES.join(" ,") << endl;
         continue;
      }
      break;
   }
   QStringList objdata;
   switch (TYPES.indexOf(typestr)) {
   case BOOK: objdata = promptBook();
         break;
   case REFERENCEBOOK: objdata = promptReferenceBook();
         break;
[ . . . . ]

   default:
         qDebug() << "Bad type in enterData()";
   }
   objdata.prepend(typestr);
   add(lib, objdata);
}

Example 6.42. src/pointer-container/libraryClient.cpp

[ . . . . ]

int main() {
   Library lib;
   while(1) {
      switch(nextTask()) {
      case READ: read(lib);
         saved = false;
         break;
      case ADD: enterData(lib);
         saved = false;
         break;
      case FIND: find(lib);
         break;
      case REMOVE: remove(lib);
         saved = false;
         break;
      case SAVE: save(lib);
         saved = true;
         break;
      case LIST: list(lib);
         break;
      case QUIT: prepareToQuit(lib);
         break;
      default:
         break;
      }
   }
}