Appendix F. Solutions

Section 1.12.1: Identifiers, Types, and Literals review questions.

(Section 1.12.1)

  1.  

    Example F.1.  src/early-examples/literals-x1.cpp

    #include <iostream>
    using namespace std;
    
    int main() {
        cout << " GNU stands for \"GNU\'s Not Unix\"." << endl;
        return 0;
    }
    
    
              Output:
    
    OOP> g++ -Wall literals.cc
    OOP> a.out
     GNU stands for "GNU's Not Unix". 
    OOP>
    
    
            

    <include src="src/early-examples/literals-x1.cpp" href="src/early-examples/literals-x1.cpp" role="solution" mode="cpp"/>


  2.  

    Example F.2.  src/early-examples/literals-x2.cpp

    #include <iostream>
    using namespace std;
    int main() {
        cout << "\n\tTitle 1\t\t\"Cat Clothing\"\n\tTitle 2\t\t\"Dog Dancing\"\n" << endl;
        return 0;
    }
    
              Output:
    
            Title 1         "Cat Clothing"
            Title 2         "Dog Dancing"
    
    
            

    <include src="src/early-examples/literals-x2.cpp" href="src/early-examples/literals-x2.cpp" role="solution" mode="cpp"/>


Section 1.13.3: C++ Simple Types review questions.

(Section 1.13.3)

  1.  

    Example F.3.  solution/simpleTypesGame/game.cpp

    /*
    Board game rules:
       1. Obtain boardSize from command line or from user.
    	2. Designate two spaces for theGoal and thePit.
    	3. while (not gameOver)
    	4.   
    	       Player[i] rolls dice and has new position.
    	        if(Goal or Pit)
    			      gameOver is true
    					announce winner.
    			  else
    			      describe and record new position of Player[i]
    */
    #include <QTextStream>
    #include <QCoreApplication>
    #include <QStringList>
    #include <cstdlib>
    
    QTextStream cout(stdout);
    QTextStream cin(stdin);
     
    int diceRoll() {
       static const int FACES(6);
       int die1, die2;
       die1 = 1 + rand() % FACES;
       die2 = 1 + rand() % FACES;
       return die1 + die2;
    }
    
    int pitOrGoal(int spaces) {
       int res;
       while (1) {
          res = rand() % (spaces);
          if(res > 1)
             return res;
       }
    }
    
    int getSpaces(int argc, QCoreApplication& app) {
       const int MINSPACES(5);
       int spaces(0);
       QString ans;
       if(argc > 1) {
          QStringList arglst = app.arguments();
          spaces = arglst[1].toInt();
       }
       if(spaces < 5) {
          while(1) {
             cout << "How many spaces are on the game board? " << flush;
             ans = cin.readLine();
             spaces = ans.toInt();
             if(spaces > MINSPACES) 
                return spaces;
             else
                cout << "At least 5 spaces please ..." << endl;
          }
       }
       return spaces;
    }
    
    int currentGame(int spaces, int pit, int goal, bool continuous) {
       int pos0(0), pos1(0);  //Both players start on first space.
       QString ans;
       int roll;
       while(1) {
          roll = diceRoll();
          cout << "You throw " << roll << '\t' << flush;
          if(not continuous){
             ans = cin.readLine();
          }
          pos1 = (pos1 + roll) % spaces;
          if(pos1 == pit) {
             cout << "You fell into the pit - you lose!" << endl;
             return 0;
          }
          if(pos1 == goal) {
             cout << "You reached the goal! You win this one!" << endl;
             return 1;
          }
          cout << "You are now on space " << pos1 << endl;
          roll = diceRoll();
          cout << "I throw " << roll << '\t' << flush;
          if(not continuous){
             ans = cin.readLine();
          }
          pos0 = (pos0 + roll)% spaces;
          if(pos0 == pit){
             cout << "Arrgh! I'm in the pit - you win this one!" << endl;
             return 1; 
          }
          if(pos0 == goal) {
             cout << "I reached the goal!  I win this one!" << endl;
             return 0;
          }
          cout << "I am now on space " << pos0 << endl;
       }
    }
    
    
    int main(int argc, char** argv) {
       QCoreApplication app(argc, argv);
       int spaces(getSpaces(argc, app));
       srand(time(0));   //Set the seed for the rand() function.
       int pit(pitOrGoal(spaces));
       int goal(pitOrGoal(spaces));
       while(pit == goal)  // pit and goal must be separate spaces
          goal = pitOrGoal(spaces);
       cout << "The goal is on space " << goal << " and the pit is on space " << pit
            << endl;
       QString ans;
       cout << "Do you want continuous play (y/N)? " << flush; //No is default.
       ans = cin.readLine();
       bool continuous = (ans.toLower().startsWith('y'));
       // Computer is player0, human is player1
       int wins0(0), wins1(0), who;
       do {
          who = currentGame(spaces, pit, goal, continuous);
          if(who == 0)
             ++wins0;
          else
             ++wins1;
          cout << "So far: You have won " << wins1 << " games, I have won "
               << wins0 << " games.\n" << "Play another (y/N)? " << flush;
          ans = cin.readLine();
       } while(ans.toLower().startsWith('y'));
       cout << "Thanks for playing with me ... I get kinda lonely sometimes."
            << endl;
    }
    
    

    <include src="solution/simpleTypesGame/game.cpp" href="solution/simpleTypesGame/game.cpp" role="solution" mode="cpp"/>


Chapter 1: C++ Introduction review questions.

(Section 1.18)

  1.  

    A stream is an object which you can use for input or output. It accepts other objects via the insertion or extraction operators (<< and >>). A stream can be attached to a file. An input stream can be attached to the keyboard (such as standard input, by default). An output stream can be attached to the program console (as standard output and standard error are, by default). A stream can also be attached to a string.

  2.  

    So that you can format and write to strings in memory, without sending anything to standard output/error.

  3.   both operations skip leading whitespace, but the >> operator will stop reading when it encounters the first whitespace, while getline gets an entire line of input.

    1.   double

    2.   char

    3.   const char*

    4.  int

    5.   float

    6.   const char*

    7.   bool

  4. Example F.4.  src/types/types-soln.cpp

    #include <QTextStream>
    
    int main() {
    	QTextStream cout(stdout);
    	int i = 5;
    	int j=6;
    	int* p = &i;   1
    
    	int& r=i;      
    	int& rpr=(*p);
    	i = 10;
    	p = &j;       2
    	rpr = 7;      3
    
    	r = 8;        4
    	cout << "i=" << i << " j=" << j << endl; 5
    	return 0;
    }
    

    1

    *p: 5

    2

    *p: 6

    3

    *p: 6

    4

    rpr: 8

    5

    i: ________ j: ________

    <include src="src/types/types-soln.cpp" href="src/types/types-soln.cpp" role="solution" mode="cpp" condition="solution"/>


  5.  

    A pointer is a variable that stores the address of another variable. It must be dereferenced before it can access the value held by the other variable.

    A reference is an alias for another variable. It can be used the same way that the variable can (unless the reference was declared to be const).

  6.  

    They are the arguments which were passed in from the command-line when running this program. argc is the number of command-line arguments, argv is an array of the actual arguments as char arrays (C-style strings).

Section 2.15: Exercise: Classes

(Section 2.15)

    1.  

      The first error:

        thing.h:10: error: 'int Thing::m_Number' is private
        thing-demo.cpp:18: error: within this context
        

      is an access error. Thing::m_Number is visible but not accessible. The second error:

        thing-demo-err.cpp:20: error: 'i' was not declared in this scope
        

      is a visibility error. The variable i does not exist where it is being invoked.

    2.   Add public "getters" for the two data members.

        int getNumber()  const { return m_Number; }
        int getCharacter() const { return m_Character; }
        

  1.   The design above makes it very difficult to implement these methods because we are copying Employer objects into Person objects. There is no way to maintain identity. We would need pointers or lists of pointers between objects in order to have proper relationships.

    1.   Two: Numbered 108, and 105.

    2.   Because the object t1 was passed by value to function, copied into temporary t. Therefore, t was created and destroyed, and its id was also 101.

Chapter 2: Top of the class review questions.

(Section 2.16)

  1.   Basically, they are the same except for the default access level of members (public for struct, private for class). Classes and structs can both have member functions, constructors, etc. However, as soon as you add member functions and access specifiers, a struct becomes more "class-like".

  2.   Class scope is accessible inside other class members. block scope is accessible only inside that block.

  3.   When enforcing creational rules, or for global operator functions.

  4.  

    A static data member of a class T is created and initialized when the program begins execution. It exists even if no T objects have been created. It is shared by all existing T objects. It is destroyed when the program terminates.

    A non-static data member of a class T is created and initialized only when a T object is created. It belongs exclusively to one object. It is destroyed when that object is destroyed.

  5.  

    A static member function of a class T cannot access any non-static data members of T. If it is public, it can be accessed by client code with the scope resolution operator T::, or through an object/pointer/reference.

    A non-static member function of a class T can access any data members of T. If it is public, it can be accessed by client code through an object (directly, or indirectly via pointer/reference).

  6.   The member function is not allowed to change any member of *this.

  7.  

    This would be a compile error. If the compiler allowed that version of the copy constructor, then imagine what would happen during a pass-by-value. Because this copy constructor takes a value parameter, it would need to call itself to pass its own parameter, and this would result in an infinite recursion.

Section 3.4: Exercises: Introduction to Qt

(Section 3.4)

  1. solution/basicio/milespergallon

  2. solution/basicio/birthday

Chapter 3: Introduction to Qt review questions.

(Section 3.5)

  1.  The QTextStream class provides a convenient interface for reading and writing text. So far we have used QTextStreams to move data to or from a text file, to standard out (the screen), from standard in (the keyboard) and to a QString.

  2.   It specifies which Qt modules will be enabled for this project. Possible values are gui, xml, sql, and net. core is enabled by default.

  3.   <QTextStream> works with Unicode QStrings and other Qt types. iostream does not work with any Qt types.

Section 4.4: Exercise: Relationships

(Section 4.4)

    You can find the complete solution in solutions.zip:lists/employer.

Chapter 4: Lists review questions.

(Section 4.5)

  1.  

    An object or programming structure that provides indirect access to each element in a container, without being bound to a specific underlying type.

    Examples: STL-style iterators, Java-style Iterators, and foreach loops.

  2.   In a composition relationship one object (the one closest to the filled diamond) manages the object(s) at the other end (called components). The manager has responsibility for destroying the component(s)as part of its own destruction. In an aggregate relationship, the lifetimes of the objects on either end are not related to one another.

  3.  

    void append ( const T & value )
    void prepend ( const T & value )
    void push_back ( const T & value )
    void push_front ( const T & value )
    QList<T> & operator<< ( const QList<T> & other )
    QList<T> & operator<< ( const T & value )
    QList<T> & operator+= ( const QList<T> & other )
    QList<T> & operator+= ( const T & value )
    

  4.  

    QString join(const QString& separator) const
    QStringList& replaceInStrings(const QRegExp& rx, const QString& after)
    void sort()
    

Section 5.1.1: Overloading Functions review questions.

(Section 5.1.1)

  1.  

    We get an ambiguous function-call message like this:

        
    function-call.cpp:43: error: call of overloaded 'demo(double)' is ambiguous
     void SignatureDemo::demo(int) <near match>
     Candidates are: void SignatureDemo::demo(int) const
                     void SignatureDemo::demo(short int) <near match>
                     void SignatureDemo::demo(float) <near match>
                     void SignatureDemo::demo(float) const
                     void SignatureDemo::demo(double) <near match>
    

    There are many possibilities listed because this is not a promotion, but a demotion, and there are two possible ways to convert with loss of data: to int or float. In addition, there are near match candidates which break const rules but are listed as possibilities anyway.

Section 5.9.1: Overloading on const review questions.

(Section 5.9.1)

  1.  

    1. double operator[](int index) const;

    2. double& operator[](int index);

    3. same as 2.

    4. same as 2.

  2.   Because only const versions can be called on pt2, and there is no const version that returns a reference.

Section 5.12: Exercise: Encryption

(Section 5.12)

  1.  

    Example F.5.  solution/encryption/crypto/randfunc.cpp

    [ . . . . ]
    
    int myRand(int min, int max) {
        return min + random() % (1 + max - min) ;
    }
    

    <include src="solution/encryption/crypto/randfunc.cpp" href="solution/encryption/crypto/randfunc.cpp" role="solution" mode="cpp" segid="myRand"/>


  2.  

    Example F.6.  solution/encryption/crypto/randfunc.cpp

    [ . . . . ]
    
    void showVect(QVector<int> vec) {
        int vsize = vec.size();
        for (int i = 0; i < vsize; ++i)
            cout << vec[i] << "  ";
        cout << endl;   
    }
    
     //Here is one way to obtain a random permutation of the numbers 0, ..., n :
     QVector<int> randomPerm(int n, unsigned key) {
         QVector<int> newperm(n) ; 
         newperm.fill(-1);
         srandom(key);
         int k;
         for (int i = 0; i < n; ++i) {
             while (1)  {
                 k = myRand(0, n-1);
                 if (newperm[k] != -1) continue;
                 newperm[k] = i;
                 break;
             }
         }
         return newperm;
     }
    

    <include src="solution/encryption/crypto/randfunc.cpp" href="solution/encryption/crypto/randfunc.cpp" role="solution" mode="cpp" segid="randomPerm"/>


  3.  

    Example F.7.  solution/encryption/crypto/randfunc.cpp

    [ . . . . ]
    
    /* shift() works with the QChars of the given QString */
    QString shift(const QString& text, unsigned key) {
         const ushort P(127);   // The size of the ASCII char set.
         srandom(key);
         QString cryptstr;
         ushort ccode, shiftcode;
         int rnd;
         for (int i = 0; i < text.length(); ++i) {
             rnd = random();
             ccode = text[i].unicode();
             shiftcode = (ccode + rnd) % P ;
             cryptstr[i] = QChar(shiftcode) ;
         }
         return cryptstr;
     }
    

    <include src="solution/encryption/crypto/randfunc.cpp" href="solution/encryption/crypto/randfunc.cpp" role="solution" mode="cpp" segid="shift"/>


     

    Example F.8.  solution/encryption/crypto/randfunc.cpp

    [ . . . . ]
    
      QString unshift(const QString& text, unsigned key) {
         const ushort P(127); 
         srandom(key);
         QString cryptstr;
         int temp;
         ushort ccode, uscode;
         int rnd;
         for (int i = 0; i < text.length(); ++i) {
             rnd = random();
             ccode = text[i].unicode();
             temp = (ccode - rnd) % P ;  // may be negative
             temp = (temp + P) % P;   // make sure it's positive
             uscode = temp;   // convert back to ushort
             cryptstr[i] = QChar(uscode) ;
         }
         return cryptstr;
     }
    

    <include src="solution/encryption/crypto/randfunc.cpp" href="solution/encryption/crypto/randfunc.cpp" role="solution" mode="cpp" segid="unshift"/>


     

    Example F.9.  solution/encryption/crypto/randfunc.cpp

    [ . . . . ]
    
     QString permute(const QString& text, unsigned key) {
         int slen = text.length();
         QVector<int> perm = randomPerm(slen, key);
         QString scrstr;
         for (int i = 0; i < slen; ++i) 
             scrstr += text.at(perm[i]);
         return scrstr;
     }
     

    <include src="solution/encryption/crypto/randfunc.cpp" href="solution/encryption/crypto/randfunc.cpp" role="solution" mode="cpp" segid="permute"/>


     

    Example F.10.  solution/encryption/crypto/randfunc.cpp

    [ . . . . ]
    
     QString unpermute(const QString& text, unsigned key) {
         int slen = text.length();
         QVector<int> perm = randomPerm(slen, key);
         QString uscrstr;
         for (int i = 0; i < slen; ++i) 
             uscrstr[perm[i]] = text[i];
         return uscrstr;
     }
     

    <include src="solution/encryption/crypto/randfunc.cpp" href="solution/encryption/crypto/randfunc.cpp" role="solution" mode="cpp" segid="unpermute"/>


     

    Example F.11.  solution/encryption/crypto/randfunc.cpp

    [ . . . . ]
    
     int main()  {
         QString str1 ("This is a sample string"), str2;
         cout << "Original string: " << str1 << endl;
         str2 = shift(str1, 1234);
         cout << "Shifted string: " << str2 << endl;
         cout << "Recovered string: " << unshift(str2, 1234) << endl;
         const int vsize(20);
         QVector<int> vec(vsize);
         vec = randomPerm(vsize, 1234);
         showVect(vec);
         str2 = permute(str1, 1234);
         cout << "Original string permuted: " << str2 << endl;
         cout << "Unpermuted string: " << unpermute(str2, 1234) << endl;
         QString str3 = shift(str2, 1234);
         cout << "Shifted, permuted string: " << str3 << endl;
         cout << "Recovered string: " << unpermute(unshift(str3, 1234), 1234)
                 << endl;
    }
     

    <include src="solution/encryption/crypto/randfunc.cpp" href="solution/encryption/crypto/randfunc.cpp" role="solution" mode="cpp" segid="testcode"/>


  4.  

    Example F.12.  solution/encryption/cryptoclass/crypto.cpp

    #include "crypto.h"
    #include <stdlib.h>  // for random()
    
    Crypto::Crypto(ushort key, QString opseq, ushort charsetsiz) 
    : m_Key(key), m_OpSequence(opseq), m_CharSetSize(charsetsiz) { }
    
    QString Crypto::encrypt(const QString& str) {
        randomPerm(str.length());
        QString oldstr(str), newstr;
        for (ushort i = 0; i < m_OpSequence.length(); ++i) {
            if (m_OpSequence[i] == 's')
                newstr = shift(oldstr);
            else
                newstr = permute(oldstr);
            oldstr = newstr;
        }
        return newstr;
    }
    
    QString Crypto::decrypt(const QString& str) {
        randomPerm(str.length());
        QString oldstr(str), newstr;
        for (ushort i = m_OpSequence.length(); i > 0;  --i) {
            if (m_OpSequence[i -1] == 's')
                newstr = unshift(oldstr);
            else
                newstr = unpermute(oldstr);
            oldstr = newstr;
        }
        return newstr;  
    }
    
    
    /* shift() works with the QChars of the given QString */
    QString Crypto::shift(const QString& text)  const{
        srandom(m_Key);
        const ushort P(m_CharSetSize);
        QString cryptstr;
        ushort ccode, shiftcode;
        int rnd;
        for (int i = 0; i < text.length(); ++i) {
            rnd = limitedRand(m_CharSetSize);
            ccode = text[i].unicode();
            shiftcode = (ccode + rnd) % P ;
            cryptstr[i] = QChar(shiftcode) ;
        }
        return cryptstr;
    }
    
    QString Crypto::unshift(const QString& text)  const{
       srandom(m_Key);
       const ushort P(m_CharSetSize);
       QString cryptstr;
       ushort ccode, shiftcode;
       int rnd;
       for (int i = 0; i < text.length(); ++i) {
          rnd = limitedRand(m_CharSetSize);
          ccode = text[i].unicode();
          shiftcode = ((ccode + P) - rnd) % P ;  // make sure it's positive
          cryptstr[i] = QChar(shiftcode) ;
       }
       return cryptstr;
    }
    
    int Crypto::limitedRand(int max) {
       return random() % max ;
    }
    
    void sequenceFill(QVector<int>& vec) {
       for (int i = 0; i < vec.size(); ++i) 
           vec[i] = i;
    }
    
    void swap(int& x, int& y) {
        int temp;
        temp = x;
        x = y;
        y = temp;
    }
    
    void Crypto::randomPerm(int n)  {
        const int numswaps(2 * n);
         m_Perm.clear(); 
         m_Perm.resize(n);
         sequenceFill(m_Perm);
         srandom(m_Key);
         int j, k;
         for (int i = 0; i < numswaps; ++i) {
                 j = limitedRand(n);
                 k = limitedRand(n);
                 swap(m_Perm[j], m_Perm[k]);
         }
    }
    
    QString Crypto::permute(const QString& text) {
        int slen = text.length();
        QString scrstr;
        for (int i = 0; i < slen; ++i) 
            scrstr += text.at(m_Perm[i]);
        return scrstr;
    }
    
    QString Crypto::unpermute(const QString& text) {
        int slen = text.length();
        QString uscrstr;
        for (int i = 0; i < slen; ++i) 
            uscrstr[m_Perm[i]] = text[i];
        return uscrstr;
     }
    
    
    

    <include src="solution/encryption/cryptoclass/crypto.cpp" href="solution/encryption/cryptoclass/crypto.cpp" role="solution" mode="cpp"/>


    1.  

      Example F.13.  solution/encryption/usermanager/usermanager.h

      #ifndef USER_MANAGER_H_
      #define USER_MANAGER_H_
      
      #include "crypto.h"
      #include <QStringList>
      
      class UserManager {
        public:
          UserManager(ushort cryptoKey, QString opseq, ushort charsiz = 128);
          bool changePW(QString userid, QString oldPW, QString newPW);
          bool addUser(QString userid, QString pw);
          bool checkUser(QString userid, QString pw);
          int saveList() const;
          int loadList();
          void listUsers() ;  /*For debugging purposes.*/
        private:
          Crypto m_Cryptor;
          QStringList m_useridList; 1
          QStringList m_pwList;  2
          int findUser(QString userid, const QString& pw) ;
      };
      
      #endif
      
      

      1

      stores userids

      2

      stores encrypted passwords

      <include src="solution/encryption/usermanager/usermanager.h" href="solution/encryption/usermanager/usermanager.h" role="solution" mode="cpp"/>


    2.  

      Example F.14.  solution/encryption/usermanager/usermanager.cpp

      #include "usermanager.h"
      #include <QFile>
      #include <QTextStream>
      
      UserManager::
      UserManager(ushort cryptoKey, QString opseq, 
                  ushort charsiz1)  :    
          m_Cryptor(cryptoKey, opseq, charsiz) { }
      
      int UserManager::
      findUser(QString userid, const QString& pw) {
          int indx, size(m_useridList.size());
          QString cryptpw(m_Cryptor.encrypt(pw));
          for (indx = 0; indx < size; ++ indx) 
              if (userid == m_useridList.at(indx) && 
                 cryptpw == m_pwList.at(indx)) 
                  return indx;
          return -1;
      }
      
      bool UserManager::
      changePW(QString userid, QString oldPW, 
               QString newPW)  {
          int indx = findUser(userid,  oldPW);
          if (indx == -1) return false;
          m_pwList[indx] = m_Cryptor.encrypt(newPW);
          return true;
      }
          
      bool UserManager::
      addUser(QString userid, QString pw) {
          // First make sure userid does not already exist.
          if (findUser(userid, pw) != -1) return false;
          m_useridList.append(userid);
          m_pwList.append(m_Cryptor.encrypt(pw));
          return true;
      }
      
      bool UserManager::
      checkUser(QString userid, QString pw) {
          int indx = findUser(userid, pw);
          return indx != -1;
      }
      
      int UserManager::
      saveList() const {
          QString separator("***ZONE***");
          QString userids(m_useridList.join(separator));
          QString passwords(m_pwList.join(separator));
          QFile outfile("pwfile");
          if (outfile.open(QFile::WriteOnly)) {
              QTextStream of(&outfile);
              of << userids << '\n' ;
              of << passwords << endl;
              outfile.close();
              return m_useridList.size();
          }
          else
              return 0;
      }
      
      int UserManager::
      loadList() {
          QString separator("***ZONE***");
          QString userids;
          QString passwords;
          QFile infile("pwfile");
          if (infile.open(QFile::ReadOnly)) {
              QTextStream inf(&infile);
              userids = inf.readLine();
              passwords = inf.readLine();
              m_useridList.clear();
              m_useridList = userids.split(separator);
              m_pwList.clear();
              m_pwList = passwords.split(separator);
              if (m_useridList.size() == m_pwList.size())
                  return m_useridList.size();
              return 0;
          }
          else
              return 0;
      }
      
      void UserManager::   2
      listUsers()  {
          QTextStream cout(stdout, QIODevice::WriteOnly);
          QTextStream cin(stdin, QIODevice::ReadOnly);
          int count(m_useridList.size());
          cout << "userid\tpassword\n" ;
          for (int i=0; i < count; ++i)
              cout << m_useridList[i] << '\t' 
                      << m_Cryptor.decrypt(m_pwList[i]) << '\n';
          cout << endl;
      }
      

      1

      = 128

      2

      for debugging purposes

      <include src="solution/encryption/usermanager/usermanager.cpp" href="solution/encryption/usermanager/usermanager.cpp" role="solution" mode="cpp"/>


    3.  

      Example F.15.  solution/encryption/usermanager/usermanager-test.cpp

      #include "usermanager.h"
      #include <QTextStream>
      QTextStream cout(stdout, QIODevice::WriteOnly);
      QTextStream cin(stdin, QIODevice::ReadOnly);
      enum  Choices {LOAD = 1, ADD, CHANGE, CHECK, SAVE, LIST, QUIT}; 1
      
      Choices menu() {
          QString str;
          int n;
          do {
              cout << LOAD      << "\tLoad User/PW list from file\n"
                      << ADD    << "\tAdd users to the list\n"
                      << CHANGE << "\tChange a user's password\n"
                      << CHECK  << "\tCheck a user's password\n"
                      << SAVE   << "\tSave User/PW list to file\n"
                      << LIST   << "\tShow all userids and passwords\n"
                      << QUIT   << "\tQuit the program\n"
                      << "Your choice: " << flush;
              str = cin.readLine();
              n = str.toInt();
          } while ( n < LOAD || n > QUIT);
          return static_cast<Choices>(n); 2
      }
      
      QString promptUserid() {
          QString userid;
          int len;
          const int MINLEN(6), UMAXLEN(8);
          while (1) {
              cout << "User ID (" << MINLEN << " to " 
                      << UMAXLEN << " chars - no spaces): " 
                      << flush ;
               userid = cin.readLine();
               len = userid.length();
               if (len < MINLEN || len > UMAXLEN ||
                   userid.contains(' ')) {
                    cout << "Invalid userid!" << endl;
                    continue;
               }
               return userid;
          } 
      }
      
      QString promptPW(bool repeat = true) {
          QString  pw1, pw2;
          int len;
          const int MINLEN(6), PMAXLEN(12);
          while (1) {
               cout << "Password (" << MINLEN << " to " 
                        << PMAXLEN << " chars): " << flush;
               pw1 = cin.readLine();
               len = pw1.length();
               if (len < MINLEN || len > PMAXLEN) {
                   cout << "Invalid password!" << endl;
                   continue;
               }
               if (repeat) {
                   cout << "Retype password: " << flush;
                   pw2 = cin.readLine();
                   if (pw1 != pw2) {
                       cout << "Passwords do not match!" << endl;
                       continue;
                   }
               }
               return pw1;
          }
      }
      
      void addUsers(UserManager& um) {
          QString userid, pw, ans;
          do {
              userid = promptUserid();
              pw = promptPW();
              cout << "Userid " << userid ;
              if (not um.addUser(userid, pw)) 
                  cout << " already exists. \n" ;
              else
                  cout << " added to list. \n" ;
              cout << "Add another user (y/n)? " << flush;
              ans = cin.readLine().toUpper();
          } while (ans[0] == 'Y');
      }
      
      void changePassword(UserManager& um) {
          QString userid(promptUserid());
          cout << "Current password ..." << endl;
          QString oldpw(promptPW(false));
          cout << "New password ..." << endl;
          QString newpw(promptPW());
          um.changePW(userid, oldpw, newpw);
      }
      
      void checkUser(UserManager& uw) {
          QString userid(promptUserid());
          QString pw(promptPW(false));
          if (uw.checkUser(userid, pw))
              cout << "User " << userid << " can login." <<endl;
          else
              cout << "Invalid userid or password." << endl;
      }
      
      
      int main() {
          QString seqstr("pspsps");
          ushort key(13579);
          UserManager um(key, seqstr);
          while (1) {
              switch (menu()) {
                 case LOAD:  
                      cout << "Reading from file ...\n"
                              << um.loadList() << " loaded to list"
                               << endl;
                      break;
                  case ADD:
                      cout << "Adding users to the list ..." << endl;
                      addUsers(um);
                      break;
                  case SAVE:
                      cout << "Saving changes ...\n"
                             << um.saveList() << " users in file" << endl;
                      break;
                  case CHANGE:
                      cout << "Changing password ..." << endl;
                      changePassword(um);
                      break;
                  case CHECK:
                      cout << "Checking a userid/pw combo ..." << endl;
                      checkUser(um);
                      break;
                  case LIST:
                      cout << "Listing users and passwords ...\n";
                      um.listUsers();
                      break;
                  case QUIT:
                      cout << "Exiting the program ..." << endl;
                      return 0;
                 default:
                      cout << "Invalid choice! " << endl;
              }
          }
      }
      
      

      1

      Enums are discussed in Chap. 19.

      2

      Casts are discussed in Chap. 19

      <include src="solution/encryption/usermanager/usermanager-test.cpp" href="solution/encryption/usermanager/usermanager-test.cpp" role="solution" mode="cpp"/>


Chapter 5: Functions review questions.

(Section 5.13)

  1.   A declaration is for describing how a function can be called. A definition includes the actual code that is to be executed.

  2.   Because it describes how the function can be called (interface), not how the function is implemented.

  3.   Nonmember global opreators are preferred because then the first or second operand can be converted just as easily to a Fraction. As a member function, only the right hand side is convertible, which means the operator won't work in a symmetric way.

  4.   Because you can and should return *this, which is not a reference to a temporary, a member function operator is the most appropriate. This permits chaining.

  5.  

    A value parameter is a local variable in a function that holds a copy of a particular argument object that was passed to the function. Changes to that variable due to the action of the function have no effect on the argument object.

    A reference parameter is an alias for a particular argument object that was passed to the function. No copy is made of the argument object. The alias is local to the function and, if it is not declared const, can permit the action of the function to change the argument object.

Section 6.10.1: Containers of Pointers review questions.

(Section 6.10.1)

  1.  

    Example F.16.  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(); 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);
    };
    
    #endif
    
    

    1

    A container of pointers needs a destructor!

    <include src="solution/library-stdio/library-v2/library.h" href="solution/library-stdio/library-v2/library.h" role="solution" mode="cpp"/>


    Example F.17.  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"/>


  2.  

    Example F.18.  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 F.19.  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"/>


Chapter 6: Inheritance and Polymorphism review questions.

(Section 6.11)

  1.   A method is a virtual function. A method call is resolved at runtime, a function call can be determined at compile-time.

  2.   The constructors, destructors, copy and assignment operators are all not inherited. They get generated for classes depending on certain rules, and the generated versions call base class versions.

  3.   If new items end up with values of existing items (e.g., if they were inserted alphabetically into the enum), data in existing files could have their m_Category member incorrectly cast. This can be avoided by making sure that existing enumerators retain their values. A simple rule to avoid the problem would be: New enumerators must always be added at the end of the enum list.

Chapter 7: Libraries and Design Patterns review questions.

(Section 7.5)

  1.   implementation - they should be only compiled once, and not included by other modules that use it.

      usually the header file, to refer to functions defined in other separate implementation files.

      header files, like most declarations.

      implementation file, just like functions. We do not wish to have redundant storage allocation for statics.

      These go in the header file.

      header file - usually they are forward declarations.

      header files - unlike regular functions which can be linked together, inline functions need to be fully defined before they can be used.

     Header file - Rarely used for member functions except when the declaration is separated from the definition - but kept in the same header file.

      header file - default arguments to a function changes the way the function is called. This is the realm of the header file.

  2.   A compile time dependency exists if we need to #include the header file in order to reuse the symbol. A link time dependency exists if we can get away with just a declaration of the symbol, which requires the linker to be able to find the referenced symbol.

  3.   Most design patterns describe how to separate code by responsibility. Each has a pattern name, a description of the problem to which the pattern applies, a description of how the pattern solves the problem, and a discussion of the consequences of employing the pattern.

Section 8.2.2.1: QObject's Child Managment review questions.

(Section 8.2.2.1)

  1.   Alice does not get destroyed until after the program terminates.

  2.  

    Example F.20.  src/iteration/java/showtree.cpp

    [ . . . . ]
    
    void showTree(QObject* theparent) {
    
        QObjectList kids = theparent->children();
    
        QListIterator<QObject*> itr (kids); 1
        while (itr.hasNext()) {  2
            QObject* ptr = itr.next();
            qDebug() << QString("Brady member: %1 - Parent: %2")
                   .arg(ptr->objectName())
                   .arg(ptr->parent()->objectName());
            showTree(ptr);
        }
    }
    

    1

    read-only java-style iteration

    2

    Java-Style iterators point between elements. The first call to next() returns the first element and advances the iterator

    <include src="src/iteration/java/showtree.cpp" href="src/iteration/java/showtree.cpp" role="solution" mode="cpp" segid="showtree"/>


Chapter 8: QObject, QApplication, Signals, and Slots review questions.

(Section 8.9)

  1.   B is managed by A, and B will be destroyed when A is.

  2.   Those that are allocated on the stack.

  3.   It is removed from its former parent's child list, and added to the new parent's child list.

  4.   To prevent it from being copied. QObjects are supposed to have identity, and so there should be no "exact" copy.

  5.   When it has both parents and children.

Section 9.6.3: Layout of Widgets review questions.

(Section 9.6.3)

  1.   solution/layout/buttonrows/buttonrows.ui

Section 9.8: Exercise: Input Forms

(Section 9.8)

  1.   solutions/forms/manual

  2.   solutions/forms/designer

Chapter 9: Widgets and Designer review questions.

(Section 9.11)

  1.   They are QObjects, so they can have parents, children, properties, signals, and slots. They are QPaintDevices, so they have a height, width, depth, and can be painted on the screen.

  2.   A QLayout is a class whose sole purpose it is to arrange other QWidgets or sub-layouts. It has no visual representation itself. There are Boxes, Grids and Stacks.

  3.   When a widget is added to a layout, its parent is changed, but to the parent of the layout, not the layout itself. So the answer is "no".

  4.   Yes if it is a top-level layout.

  5.   Yes, that is how to nest layouts.

  6.   The images can get compiled into the executable and no longer need to be installed separately.

  7.   A spacer is a fixed amount of space that does not stretch, while a stretch starts with a certain amount of space and expands to gobble up available space. In the case of multiple stretches in the same layout, the ratio can be used to determine which stretch expands faster.

Section 10.7.1: Exercises: MainWindows

(Section 10.7)

  1.  

    Example F.21.  solution/textFileBrowser/main.cpp

    #include <QtGui/QApplication>
    #include "mainwindow.h"
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
    
        return a.exec();
    }
    

    <include src="solution/textFileBrowser/main.cpp" href="solution/textFileBrowser/main.cpp" role="solution" mode="cpp"/>


    Example F.22.  solution/textFileBrowser/mainwindow.cpp

    #include <QFileDialog>
    #include <QFile>
    #include <QString>
    #include <QTextStream>
    #include <QCoreApplication>
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::on_openButton_clicked() {
        QString fileName = QFileDialog::getOpenFileName();
        QFile file(fileName);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
             return;
        QTextStream in(&file);
        m_Lines.clear();
        while (!in.atEnd()) {
            QString line = in.readLine();
            m_Lines << line;
        }
        file.close();
        ui->textEdit->clear();
        ui->textEdit->setHtml(m_Lines.join("\n"));
    }
    
    void MainWindow::on_closeButton_clicked()
    {
        ui->textEdit->clear();
        m_Lines.clear();
    }
    
    void MainWindow::on_quitButton_clicked()
    {
        QCoreApplication::quit();
    }
    
    void MainWindow::on_searchEdit_returnPressed()
    {
        QString searchstr = ui->searchEdit->text();
        if(ui->textEdit->find(searchstr))
            statusBar()->clearMessage();
        else
            statusBar()->showMessage(searchstr + " not found");
    }
    
    void MainWindow::on_nextButton_clicked()
    {
        on_searchEdit_returnPressed();
    }
    
    void MainWindow::on_previousButton_clicked()
    {
        QString searchstr = ui->searchEdit->text();
        if(ui->textEdit->find(searchstr, QTextDocument::FindBackward))
            statusBar()->clearMessage();
        else
            statusBar()->showMessage(searchstr + " not found");
    }
    
    void MainWindow::on_actionOpen_triggered()
    {
        on_openButton_clicked();
    }
    
    void MainWindow::on_action_Close_triggered()
    {
        on_closeButton_clicked();
    }
    
    void MainWindow::on_action_Quit_triggered()
    {
        on_quitButton_clicked();
    }
    
    void MainWindow::on_action_Previous_triggered()
    {
        on_previousButton_clicked();
    }
    
    void MainWindow::on_action_Next_triggered()
    {
        on_nextButton_clicked();
    }
    
    void MainWindow::on_clearSearchButton_clicked()
    {
        ui->searchEdit->clear();
        ui->searchEdit->setFocus();
    }
    

    <include src="solution/textFileBrowser/mainwindow.cpp" href="solution/textFileBrowser/mainwindow.cpp" role="solution" mode="cpp"/>


    Example F.23.  solution/textFileBrowser/mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QString>
    
    namespace Ui {
        class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
    private:
        Ui::MainWindow *ui;
        QStringList m_Lines;
    
    private slots:
        void on_clearSearchButton_clicked();
        void on_closeButton_clicked();
        void on_action_Close_triggered();
        void on_action_Next_triggered();
        void on_action_Previous_triggered();
        void on_action_Quit_triggered();
        void on_actionOpen_triggered();
        void on_previousButton_clicked();
        void on_nextButton_clicked();
        void on_searchEdit_returnPressed();
        void on_quitButton_clicked();
        void on_openButton_clicked();
    };
    
    #endif // MAINWINDOW_H
    

    <include src="solution/textFileBrowser/mainwindow.h" href="solution/textFileBrowser/mainwindow.h" role="solution" mode="cpp"/>


    Example F.24.  solution/textFileBrowser/mainwindow.ui

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>MainWindow</class>
     <widget class="QMainWindow" name="MainWindow">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>767</width>
        <height>473</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>Text File Browser</string>
      </property>
      <widget class="QWidget" name="centralWidget">
       <widget class="QWidget" name="">
        <property name="geometry">
         <rect>
          <x>10</x>
          <y>1</y>
          <width>751</width>
          <height>421</height>
         </rect>
        </property>
        <layout class="QHBoxLayout" name="horizontalLayout_3">
         <item>
          <layout class="QVBoxLayout" name="verticalLayout_2">
           <item>
            <widget class="QTextEdit" name="textEdit"/>
           </item>
           <item>
            <layout class="QHBoxLayout" name="horizontalLayout_2">
             <item>
              <widget class="QLabel" name="label">
               <property name="text">
                <string>Search text</string>
               </property>
              </widget>
             </item>
             <item>
              <widget class="QLineEdit" name="searchEdit"/>
             </item>
            </layout>
           </item>
           <item>
            <layout class="QHBoxLayout" name="horizontalLayout">
             <item>
              <widget class="QPushButton" name="previousButton">
               <property name="text">
                <string>Previous</string>
               </property>
              </widget>
             </item>
             <item>
              <widget class="QPushButton" name="nextButton">
               <property name="text">
                <string>Next</string>
               </property>
              </widget>
             </item>
             <item>
              <widget class="QPushButton" name="clearSearchButton">
               <property name="text">
                <string>ClearSearch</string>
               </property>
              </widget>
             </item>
             <item>
              <spacer name="horizontalSpacer">
               <property name="orientation">
                <enum>Qt::Horizontal</enum>
               </property>
               <property name="sizeHint" stdset="0">
                <size>
                 <width>40</width>
                 <height>20</height>
                </size>
               </property>
              </spacer>
             </item>
            </layout>
           </item>
          </layout>
         </item>
         <item>
          <layout class="QVBoxLayout" name="verticalLayout">
           <item>
            <spacer name="verticalSpacer_2">
             <property name="orientation">
              <enum>Qt::Vertical</enum>
             </property>
             <property name="sizeHint" stdset="0">
              <size>
               <width>20</width>
               <height>40</height>
              </size>
             </property>
            </spacer>
           </item>
           <item>
            <widget class="QPushButton" name="openButton">
             <property name="text">
              <string>Open</string>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QPushButton" name="closeButton">
             <property name="text">
              <string>Close</string>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QPushButton" name="quitButton">
             <property name="text">
              <string>Quit</string>
             </property>
            </widget>
           </item>
           <item>
            <spacer name="verticalSpacer">
             <property name="orientation">
              <enum>Qt::Vertical</enum>
             </property>
             <property name="sizeHint" stdset="0">
              <size>
               <width>20</width>
               <height>40</height>
              </size>
             </property>
            </spacer>
           </item>
          </layout>
         </item>
        </layout>
       </widget>
      </widget>
      <widget class="QMenuBar" name="menuBar">
       <property name="geometry">
        <rect>
         <x>0</x>
         <y>0</y>
         <width>767</width>
         <height>23</height>
        </rect>
       </property>
       <widget class="QMenu" name="menuFile">
        <property name="title">
         <string>File</string>
        </property>
        <addaction name="actionOpen"/>
        <addaction name="action_Close"/>
        <addaction name="action_Quit"/>
       </widget>
       <widget class="QMenu" name="menuSearch">
        <property name="title">
         <string>Search</string>
        </property>
        <addaction name="action_Previous"/>
        <addaction name="action_Next"/>
       </widget>
       <addaction name="menuFile"/>
       <addaction name="menuSearch"/>
      </widget>
      <widget class="QToolBar" name="mainToolBar">
       <attribute name="toolBarArea">
        <enum>TopToolBarArea</enum>
       </attribute>
       <attribute name="toolBarBreak">
        <bool>false</bool>
       </attribute>
      </widget>
      <widget class="QStatusBar" name="statusBar"/>
      <action name="actionOpen">
       <property name="text">
        <string>&amp;Open</string>
       </property>
      </action>
      <action name="action_Close">
       <property name="text">
        <string>&amp;Close</string>
       </property>
      </action>
      <action name="action_Quit">
       <property name="text">
        <string>&amp;Quit</string>
       </property>
      </action>
      <action name="action_Previous">
       <property name="text">
        <string>&amp;Previous</string>
       </property>
      </action>
      <action name="action_Next">
       <property name="text">
        <string>&amp;Next</string>
       </property>
      </action>
     </widget>
     <layoutdefault spacing="6" margin="11"/>
     <resources/>
     <connections/>
    </ui>

    <include src="solution/textFileBrowser/mainwindow.ui" href="solution/textFileBrowser/mainwindow.ui" role="solution" mode="txt"/>


    Example F.25.  solution/textFileBrowser/textBrowser.pro

    #-------------------------------------------------
    #
    # Project created by QtCreator 2011-04-11T18:29:14
    #
    #-------------------------------------------------
    
    QT       += core gui
    
    TARGET = textBrowser
    TEMPLATE = app
    
    
    SOURCES += main.cpp\
            mainwindow.cpp
    
    HEADERS  += mainwindow.h
    
    FORMS    += mainwindow.ui

    <include src="solution/textFileBrowser/textBrowser.pro" href="solution/textFileBrowser/textBrowser.pro" role="solution" mode="txt"/>


Chapter 10: MainWindows and Actions review questions.

(Section 10.8)

  1.   A QAction is an object representing something that the user can do to the program. It can be triggered through menu items, button clicking, keyboard presses, etc.

  2.   By creating a unique QAction for each action, you can add it to different menus and toolbars, and from one place, disable/enable it, or change other properties (such as its label or tooltip) and it will reflect in all views (menuitems, toolbar buttons, etc) that refer to it.

  3.   QRunnable, QProcess, QAction, QThread, QtConcurrentRun

Section 11.6: Exercise: Generics

(Section 11.6)

  1.  

    It is possible to do this with a QMultiMap instead of a map of sets. That approach is shown in Section 13.6

    Example F.26.  solution/generics/friendslist/friendslist.h

    [ . . . . ]
    class FriendList :public QSet<QString> { };
    class FriendMap : public QHash<QString, FriendList > {
      public:
        void set(QString k1, QString k2);
        void unset(QString k1, QString k2);
        FriendList& friends(QString key);
    };
    
    
    #endif        //  #ifndef FRIENDSLIST_H
    

    <include src="solution/generics/friendslist/friendslist.h" href="solution/generics/friendslist/friendslist.h" role="solution" mode="cpp" allfiles="1"/>


    Example F.27.  solution/generics/friendslist/friendslist.cpp

    #include "engine.h"
    #include <QDebug>
    
    void FriendMap::unset(QString id1, QString id2) {
       friends(id1).remove(id2);
       friends(id2).remove(id1);
    }
    
    FriendList& FriendMap::friends(QString key) {
          FriendList& retval = operator[](key);
          if (retval.isEmpty()) {
              retval << key;
              insert(key, retval);
          }
          return retval;
    }
    
    
    void FriendMap::set(QString id1, QString id2) {
        FriendList &c1 = operator[](id1);   1
        c1 += id2;                          2
        operator[](id2) <<  id1;            3
    }
    
    

    1

    Find all the mapped values matching first key

    2

    We could have used operator<<() here

    3

    Same as above but shorter

    <include src="solution/generics/friendslist/friendslist.cpp" href="solution/generics/friendslist/friendslist.cpp" role="solution" mode="cpp"/>


    Example F.28.  solution/generics/friendslist/engine.h

    #ifndef ENGINE_H
    #define ENGINE_H
    #include "friendslist.h"
    
    
    /* An engine is a console front-end app for the FriendMap test
    program */
    
    class Engine : public QObject {
        Q_OBJECT
      public:
        void showFriends(QString symbol);
        QString processLine(QString inputline);
        void history() const;
        QString takeback(int index);   1
      private:
        QVector<QString> m_Messages;   2
        FriendMap m_friendMap;         3
    };
    
    
    #endif        //  #ifndef ENGINE_H
    

    1

    Removes a previous assertion

    2

    A log of incoming messages

    3

    Maintains a mapping of symbols to heap EquivClass objects.

    <include src="solution/generics/friendslist/engine.h" href="solution/generics/friendslist/engine.h" role="solution" mode="cpp"/>


    Example F.29.  solution/generics/friendslist/engine.cpp

    #include "engine.h"
    #include <QtCore>
    
    void Engine::showFriends(QString key) {
        qDebug() << "Friends of " << key << endl;
        FriendList& eq = m_friendMap[key];
        foreach (QString k, eq) 
            if (key != k) 
                qDebug() << QString(" [ %1=%2 ] ").arg(key).arg(k);
    }
    QString Engine::processLine(QString l) {
        if (l.startsWith("takeback")) {
            int wschar = l.lastIndexOf(' ');
            int removeIndex = l.mid(wschar).toInt();
            return takeback(removeIndex);
        }
    
        int i = l.indexOf('=');
        if (i>0) {
            QString key = l.left(i);
            QString value = l.mid(i+1);
            m_Messages.append(l);     1
            m_friendMap.set(value, key);
            m_friendMap.set(key, value);
            return key;
        }
        else {                        2
            showFriends(l);
            return l;
        }
        return QString("Unrecognized Line: %1").arg(l);
    }
    
    
    
    QString Engine::takeback(int index) {
        if (m_Messages.count() < index) {
            qDebug() << "invalid index" << endl;
            return "";
        } else {
            QString line = m_Messages[index];
            qDebug() << "Taking Back: " << line << endl;
            int i = line.indexOf('=');
            QString key = line.left(i);
            QString value = line.mid(i+1);
            m_friendMap.unset(key, value);
            return key;
        }
    }
    
    void Engine::history() const {
        //    iterator itr<QString> =
    }
    
    int main(int argc, char** argv) {
        QTextStream cout(stdout);
        QTextStream cin(stdin);
        Engine engine;
        QString currentline;
        QString key;
        do {
            cout << " > " << flush;
            currentline = cin.readLine();
            key = engine.processLine(currentline);
        } while (currentline != QString()) ;
        return 0;
    }
    
    

    1

    By adding to a vector, we maintain integer id on each message

    2

    No "=" in input line - just print out the equalities of that symbol

    <include src="solution/generics/friendslist/engine.cpp" href="solution/generics/friendslist/engine.cpp" role="solution" mode="cpp"/>


Chapter 11: Generics and Containers review questions.

(Section 11.7)

  1.   A function parameter must be a constant or a variable, while a template parameter can also be (and most commonly is) a type expression such as int, QObject, Person*.

  2.   Calling a template function with actual arguments is one way to instantiate a template function. In general, using a template definition, by providing actual template parameters, is how you can get the compiler to generate the template code for the specific type you are using.

  3.   Because, like any inline functions, because the compiler must substitute the definition in the code where the invocation is encountered, the definition must be known by the compiler. The linker will not resolve it later. The exception to this is with compilers which support export and maintain a database of template instantiations which get built along with the object modules. Such compilers blur the distinction between a compiler and a linker, but c'est la vie.

  4.   Export makes it possible for template definitions in one source module to be used in others, linked in a linker-like fashion. It helps reduce the code-size of a program, and also eliminates redundant code.

  5.   QObjects, polymorphic collections in general, and any class that can not be copied because it has no copy constructor.

  6.   QMap and QHash.

  7.   QList, QString, QSet.

  8.  Template definitions (classes and functions) must all be located in the header file. This is necessary for the compiler to generate code from a template declaration. Also the template declaration code template<class T> must precede each class or function definition that has a template parameter in its name.

Chapter 12: Meta Objects, Properties, and Reflective Programming review questions.

(Section 12.9)

  1.  

    1. className(), which returns the class name as a const char*

    2. superClass(), which returns a pointer to the QMetaObject of the base class if there is one (or 0 if there is not)

    3. methodCount(), which returns the number of member functions of the class

    4. method(index), which returns the meta-data for the method with the given index.

    5. propertyCount(), which returns the number of properties in this class, including base class properties.

    6. property(index), which returns the meta-data for the property with the given index.

  2.   QMetaObject, QMetaProperty, QSqlDatabase::tables, QSqlRecord, QVariant

  3.   moc generates QMetaObject classes to support properties, signals and slots. Normally, you do not run moc directly. It is run automatically by make on the header files listed in HEADERS which use the Q_OBJECT macro.

  4.   A downcast is a conversion from something "higher" (more general) in the inheritance graph, to a "lower" class (one of its derived types). We use downcasts in situations where we are forced to use a base class pointer (usually because a function we did not write is using that type) but we require a non-polymorphic derived class member function.

  5.   Q_PROPERTY macros make it possible for moc to generate code for QObject's property() and setProperty() member functions. The advantage of using these functions is that client code can determine which properties are available by iterating through QMetaProperty objects of the QMetaObject corresponding to that class.

  6.   property() returns a QVariant, which is a union wrapper around every possible basic type, and also several Qt classes/types. With QVariant, you can ask for its type() and convert to the actual value<>(). Benefits are most apparent when implementing script engines or developer tools. It becomes possible to define "handle anything" kinds of functions without using anything but the QObject interface to read and write values.

  7.   setProperty("propName") sets a dynamic property for that object even if it is not declared as a Q_PROPERTY

  8.   They are stored in a QVariantMap, which can be serialized via a QDataStream.

Section 13.6: Exercises: Models and Views

(Section 13.6)

  1.   See solution/modelview/filebrowser.

  2.   solution/modelview/tablemodel shows an example of how to do it reusing the dataobjecttablemodel and metadataloader classes, where the sample data is based on actual id3 tags.

  3.  

    It's a lot easier with a proxy model!

    Example F.30.  solution/modelview/friendslist-gui/friendslistmodel.h

    #ifndef FRIENDLISTMODEL_H
    #define FRIENDLISTMODEL_H
    
    #include <QAbstractListModel>
    #include <QVariant>
    #include <QMultiMap>
    #include <QString>
    #include <QStringList>
    class FriendsList : public QMultiMap<QString, QString> {
    public:
        QStringList friends(QString person) ;
        bool friendsWith(QString a, QString b) const;
    };
    class FriendsListModel : public QAbstractListModel
    {
        Q_OBJECT
    public:
        explicit FriendsListModel(FriendsList& fl, QObject *parent = 0);
        int rowCount(const QModelIndex &parent = QModelIndex()) const;
        QVariant data(const QModelIndex &index, int role) const;
        bool setData(const QModelIndex &index, const QVariant &value, int role);
        Qt::ItemFlags flags(const QModelIndex &index) const;
        QModelIndex indexOf(QString key) ;
    
        void add(QString k);    
        void set(QString k1, QString k2);
        void unset(QString k1, QString k2);
    
    public slots:
        void contactSelected(QModelIndex idx);
    
    protected:
        QString m_contact;
        FriendsList& m_friendsList;
        QStringList m_allPeople;
    };
    
    #endif // FRIENDLISTMODEL_H
    

    <include src="solution/modelview/friendslist-gui/friendslistmodel.h" href="solution/modelview/friendslist-gui/friendslistmodel.h" role="solution" mode="cpp"/>


    Example F.31.  solution/modelview/friendslist-gui/friendslistmodel.cpp

    #include "friendslistmodel.h"
    
    QStringList FriendsList::friends(QString key)  {
        QStringList retval;
        const_iterator itr = constFind(key);
        while (itr != constEnd()) {
            retval << itr.value();
            itr++;
        }
        return retval;
    }
    
    bool FriendsList::friendsWith(QString a, QString b) const {
        if (a == b) return true;
        return constFind(a, b) != constEnd();
    }
    
    FriendsListModel::FriendsListModel(FriendsList& fl, QObject *parent)
        : QAbstractListModel(parent), m_friendsList(fl) {
        // updateAllPeople();
    }
    
    Qt::ItemFlags FriendsListModel::flags(const QModelIndex &index) const {
        int r = index.row();
        if (r >= rowCount()) return 0;
        QString d = m_allPeople[r];
        
        if (d == m_contact || m_contact == QString()) 
            return 0; 1  
        return Qt::ItemIsUserCheckable | Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    }
    
    void FriendsListModel::add(QString k) {
        int pos = 0;
        if (m_allPeople.contains(k)) {
            QMessageBox::critical(0, "Duplicate Person", "Contact already exists");
            return;
        }
        if (k == QString()) return;
        for (int i = m_allPeople.length() - 1; i >= 0; --i) {
            if (k >= m_allPeople[i]) {pos = i+1; break;}
        }
        if (pos >= rowCount()) pos = rowCount();
        if (pos < 0) pos = 0;
        beginInsertRows(QModelIndex(), pos, pos);
        m_allPeople << k;
        m_allPeople.sort();
        set(k,k);
        endInsertRows();
    }
    
    
    int FriendsListModel::rowCount(const QModelIndex &parent) const {
        if (parent.isValid()) return 0;
        return m_allPeople.count();
    }
    
    QVariant FriendsListModel::data(const QModelIndex &index, int role) const {
        int r = index.row();
        if (r >= rowCount()) return QVariant();
        QString d = m_allPeople[index.row()];
        if ((role == Qt::DisplayRole) || (role == Qt::EditRole))
            return d;
        if ((role == Qt::CheckStateRole)) {
            return m_friendsList.friendsWith(d, m_contact);
        }
        return QVariant();
    }
    
    bool FriendsListModel::setData(const QModelIndex &index, const QVariant &value, int role) {
        if (role == Qt::CheckStateRole) { 2
            QString d = m_allPeople[index.row()];
            if (d == m_contact || m_contact == QString()) return false;
            bool isFriends = m_friendsList.friendsWith(d, m_contact);
            if (!isFriends) 
                set(m_contact, d);
            else 
                unset(m_contact, d);
            return true;
        }
        return false;
    }
    
    
    void FriendsListModel::contactSelected(QModelIndex idx) {
        int r = idx.row();
        QString old = m_contact;
        if (idx.isValid()) {
            m_contact = m_allPeople[r];
        }
        else m_contact = QString();
        if (old != m_contact) {
            emit dataChanged(createIndex(0,0), createIndex(rowCount(), 0));
        }
    }
    
    
    QModelIndex FriendsListModel::indexOf(QString key) {
        int r = m_allPeople.indexOf(key);
        return createIndex(r, 0);
    }
    
    void FriendsListModel::set(QString k1, QString k2) {
        m_friendsList.insert (k1, k2);
        m_friendsList.insert (k2, k1);
        int index1 = m_allPeople.indexOf(k1);
        int index2 = m_allPeople.indexOf(k2);
        emit dataChanged(createIndex(index1,0),createIndex(index1,0));
        emit dataChanged(createIndex(index2,0),createIndex(index2,0));
    }
    
    void FriendsListModel::unset(QString k1, QString k2) {
        if (k1 == k2) return;
        m_friendsList.remove(k1, k2);
        m_friendsList.remove(k2, k1);
        int index1 = m_allPeople.indexOf(k1);
        int index2 = m_allPeople.indexOf(k2);
        emit dataChanged(createIndex(index1,0),createIndex(index1,0));
        emit dataChanged(createIndex(index2,0),createIndex(index2,0));
    }
    
    

    1

    Don't let user interact with this item unless a different contact is selected on the left.

    2

    The item is not editable but we go here if the user tries to check an item that is ItemIsUserCheckable.

    <include src="solution/modelview/friendslist-gui/friendslistmodel.cpp" href="solution/modelview/friendslist-gui/friendslistmodel.cpp" role="solution" mode="cpp"/>


    Example F.32.  solution/modelview/friendslist-gui/friendslists.h

    #ifndef FRIENDSLISTS_H
    #define FRIENDSLISTS_H
    
    #include <QDialog>
    #include <QModelIndex>
    #include "friendslistmodel.h"
    #include "contactproxymodel.h"
    
    namespace Ui {
        class FriendsLists;
    }
    class FriendsLists : public QDialog
    {
        Q_OBJECT
    public:
        explicit FriendsLists(QWidget *parent = 0);
        ~FriendsLists();
    protected:
        void changeEvent(QEvent *e);
    private slots:
        void on_addButton_clicked();
    private:
        QString m_lastSelected;
        Ui::FriendsLists *ui;
        FriendsList m_friendsList;
        // list on right:
        FriendsListModel *m_cmodel;
        // list on left:
        ContactProxyModel *m_cpmodel;
    
    };
    
    #endif // FRIENDSLISTS_H
    

    <include src="solution/modelview/friendslist-gui/friendslists.h" href="solution/modelview/friendslist-gui/friendslists.h" role="solution" mode="cpp"/>


    Example F.33.  solution/modelview/friendslist-gui/friendslists.cpp

    #include "friendslistmodel.h"
    #include "friendslists.h"
    #include "ui_friendslists.h"
    #include <QtGui>
    
    FriendsLists::FriendsLists(QWidget *parent) 
    : QDialog(parent), ui(new Ui::FriendsLists) {
        ui->setupUi(this);
        m_cmodel = new FriendsListModel(m_friendsList, this);
        m_cmodel->add("a");
        m_cmodel->add("z");
        m_cmodel->add("m");
        m_cmodel->add("b");
        //m_cmodel->updateAllPeople();
        m_cpmodel = new ContactProxyModel(this);
        m_cpmodel->setSourceModel(m_cmodel);
        ui->contactsListView->setModel(m_cpmodel);
        ui->friendsListView->setModel(m_cmodel);
        connect (ui->contactsListView, SIGNAL(clicked(QModelIndex)),
                 m_cmodel, SLOT(contactSelected(QModelIndex)));
    }
    
    FriendsLists::~FriendsLists() {
        delete ui;
    }
    
    void FriendsLists::changeEvent(QEvent *e) {
        QDialog::changeEvent(e);
        switch (e->type()) {
        case QEvent::LanguageChange:
            ui->retranslateUi(this);
            break;
        default:
            break;
        }
    }
    
    void FriendsLists::on_addButton_clicked() {
        QString str = QInputDialog::getText(this, 
            tr("Enter a symbol"), tr("Symbol: ") );
        if (str == QString()) return;
        m_cmodel->add(str);
    }
    

    <include src="solution/modelview/friendslist-gui/friendslists.cpp" href="solution/modelview/friendslist-gui/friendslists.cpp" role="solution" mode="cpp"/>


    Example F.34.  solution/modelview/friendslist-gui/contactproxymodel.h

    #ifndef CONTACTPROXYMODEL_H
    #define CONTACTPROXYMODEL_H
    
    #include <QSortFilterProxyModel>
    class ContactProxyModel: public QSortFilterProxyModel
    {
        Q_OBJECT
    public:
        typedef QSortFilterProxyModel SUPER;
        explicit ContactProxyModel(QObject *parent = 0); 
        Qt::ItemFlags flags(const QModelIndex &index) const;
        QVariant data(const QModelIndex &index, int role) const;
    };
    
    #endif // CONTACTPROXYMODEL_H
    

    <include src="solution/modelview/friendslist-gui/contactproxymodel.h" href="solution/modelview/friendslist-gui/contactproxymodel.h" role="solution" mode="cpp"/>


    Example F.35.  solution/modelview/friendslist-gui/contactproxymodel.cpp

    #include "contactproxymodel.h"
    
    
    ContactProxyModel::ContactProxyModel(QObject *parent)
        : SUPER(parent) {}
    
    Qt::ItemFlags ContactProxyModel::flags(const QModelIndex &index) const {
        int r = index.row();
        if ((r < 0 ) || r >= rowCount()) return 0;
        return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    }
    
    QVariant ContactProxyModel::data(const QModelIndex &index, int role) const {
        if (role == Qt::CheckStateRole)
            return QVariant();
        else return SUPER::data(index, role);
    }
    
    

    <include src="solution/modelview/friendslist-gui/contactproxymodel.cpp" href="solution/modelview/friendslist-gui/contactproxymodel.cpp" role="solution" mode="cpp"/>


  4.   See solution/modelview/shortcutmodel-delegate

Chapter 13: Models and Views review questions.

(Section 13.7)

  1.   Code which creates, destroys, or connects objects together is considered controller code. QApplication and QAction are both containers for controller code, but the ItemDelegate is also considered a controller.

  2.   Each view has a SelectionModel which can describe which item(s) are selected in a view.

  3.   QModelIndex is a "cursor", or an indirect reference to data in a model. This can be used to iterate through items in the model.

  4.   StandardItemModel holds onto the data, while an abstract model can be a proxy for data elsewhere. StandardItemModel can lead to duplication of data. It is recommended to implement abstract methods from an AbstractItemModel for custom models.

Chapter 14: Validation and Regular Expressions review questions.

(Section 14.7)

  1.  An inputMask is a QString property of a QLineEdit which can contain mask chars to constrain the incoming text. An inputMask can specify what kinds of characters are allowed in certain positions of a string that is being typed into a QLineEdit.

  2.   A meta-character is a character that "describes" other characters. Quantifiers are ?*+{m,n} - they modify the preceeding thing with a quantity. Character Sets are things that can be replaced by other characters: .\d\D\s\S\w\W[a-z]. Grouping characters are parantheses (), which let you back-reference parts of the matched result. Anchoring characters describe what goes at the beginning or end of the pattern: ^\b\B$.

  3.   When the thing you are checking is not a simple regular expression, but something that requires advance calculation.

Chapter 15: Parsing XML review questions.

(Section 15.5)

  1.   xmllint is one way. Also, you can define an ErrorHandler in SAX, or ask the QXmlStreamReader for the line number of errors. Also, XML editors such as jEdit with the XML plugin can parse and show you where errors in XML documents are.

  2.   Start of element, end of element, characters, etc

  3.   SAX is a lower-level API, which handles individual parse events (start of element, end of elementm etc). DOM is an Object Model, which uses SAX under the covers to parse the data into an in-memory tree structure. SAX is useful if you need a way to quickly process infinitely large XML files in a stream-like way. DOM is nice if you want to load the entire tree into memory and traverse/manipulate it.

  4.   You can't use normal typecasts because QDomNode and QDomElement are not pointers. Therefore, use the toElement() function instead.

  5.   It is faster, uses less memory, and is easier to learn and use.

  6.   DOM needs to load an entire document in memory, using a very inefficient representation. With the QXmlStreamReader, you can choose to create or not create objects of any size you want as you are parsing the document.

Chapter 16: More Design Patterns review questions.

(Section 16.4)

  1.   By adding the pointer to a list of objects to be destroyed, before returning it from the Factory method. This can be achieved in a number of ways, such as adding it as a child to another object, or a managed pointer collection.

  2.   Properties can be inspected at runtime to find out what names/values need to be loaded/saved. This way, you can write one serializer for all QObjects.

  3.   With an Abstract Factory, we can "plug in" different kinds of ObjectFactory objects into the same Reader, to support different libraries of types. This helps separate the Reader code from the library-specific code.

  4.   QString, QWidget, QThread, QProcess, QSqlDatabase, QFile, and many others.

Section 17.1.4: QProcess and Process Control review questions.

(Section 17.1.4)

    1.  

      Example F.36.  solution/pwmgr/HashApp/hash.cpp

      #include <QTextStream>
      #include <QCryptographicHash>
      #include <QByteArray>
      #include <QStringList>
      #include <QCoreApplication>
      
      QTextStream cout(stdout);
      QTextStream cerr(stderr);
      
      QString usage() {
         return QString("usage:\n\t%1\n\t%2\n\t%3\n\t%4")
            .arg("encrypt string alg")
            .arg("where alg can be one of three algorithms:")
            .arg("Md4, Md5, or Sha1. If this switch is absent")
            .arg("the default will be Sha1.");
      }
      
      int main(int argc, char** argv) {
        QCoreApplication qca(argc, argv);
        QStringList al(qca.arguments());
        if(al.count() < 2) {
            cerr << usage() << endl;
            return 1;
         }
         al.takeFirst(); //Get rid of the app name.
         QByteArray qba;
         qba.append(al.takeFirst()); // String to be hashed
         
         QString algarg = al.takeFirst().toLower();
         QCryptographicHash::Algorithm alg;
         if(algarg == "md4")
            alg = QCryptographicHash::Md4;
         else if(algarg == "md5")
            alg = QCryptographicHash::Md5;
         else
            alg = QCryptographicHash::Sha1;
         cout << QCryptographicHash::hash(qba, alg) << endl;
      }
      
      

      <include src="solution/pwmgr/HashApp/hash.cpp" href="solution/pwmgr/HashApp/hash.cpp" role="solution" mode="cpp"/>


    2.  

Chapter 17: Concurrency review questions.

(Section 17.4)

  1.  

    1. Command-line arguments

    2. standard input and output streams

    3. environment variables

  2.  

    1. locks

    2. wait conditions

    3. mutexes

    4. semaphores

  3.   A QTimer is useful if the simultaneous task that needs to be performed can be implemented as an incremental algorithm that requires repeated calls to a function that does not run for a long period of time. In this situation, we can avoid the use of threads (which sometimes have problems accessing QObjects/QWidgets from other threads).

  4.  A thread-safe function is one that can be called concurrently by multiple threads and is guaranteed to serialize access to shared data, perhaps through the use of a mutex or lock.

  5.   QImage can be used in other threads. QPixmap is considered a "gui" class and should only be used in the main GUI thread.

  6.   Multiple threads can call a reentrant function safely, and they do not need to access the same data, so they do not block each other during the calls.

  7.   QThread::exec()

  8.   qApp->processEvents()

Chapter 18: Database Programming review questions.

(Section 18.4)

  1.   Sqlite

  2.   QSqlDatabase::drivers()

  3.   It is an abstraction for a database connection, not an actual database.

  4.   A DDL query creates a table, or modifies its structure.

  5.   They are faster, because the query does not need to be parsed as often. They are safer, because they are not subject to SQL injection attacks. They are easier to use with strings because there is no need to escape the characters inside them.

  6.   QSqlConnection, because it represents a connection and not an actual database.

  7.   Yes, even if the server or the driver does not support them, Qt emulates prepared queries on the client side.

  8.   Yes, some SQL queries update or insert rows into a table. A "query" is more than just a select statement.

Section 19.2.4: Statements and Control Structures review questions.

(Section 19.2.4)

  1.   a simple statement is terminated by a ; a compound statement has curly braces {} around a group of simple statements.

  2.   add a default case.

  3.   the do..while loop is useful if you want to guarantee that the body is executed at least once. The for loop is useful if you need a counter to be incremented in the loop.

Section 19.12: Exercises: Types and Expressions

(Section 19.12)

     

    What went wrong was that the class was not const-correct. showSum should receive a const Snafu& parameter, but it was passed a regular Snafu&. Even though showSum doesn't change the passed-in object, the compiler barfs.

    To fix the program, you can:

    1. make foo receive the second parameter as non-const, or

    2. Apply a const_cast to myObject2 as you pass it to showSum:

       myObject1.showSum(const_cast<Snafu&>(myObject2));
             [137]
               

  1.  

    You don't want to remove the const from print(). Anyone using this class would expect to be able to print both const and non-const objects. You cast away const-ness like this:

    ++const_cast< Quux * >( this )->printcounter;

    We relegate this to a separate function to make it very clear what is going on.

Chapter 19: Types and Expressions review questions.

(Section 19.13)

  1.   An expression has a type and a value. A statement is just something to be executed. Statements are grouped with {} while expressions are grouped with the ().

  2.   All averloaded operators have corresponding function definitions. In fact, it is possible to invoke operators using the function call syntax. Therefore, operators are just syntactic shorthand for certain functions.

  3.   class, struct, enum, typedef

  4.   static_cast

  5.   dynamic_cast

Chapter 20: Scope and Storage Class review questions.

(Section 20.5)

  1.   Scope is a region of code where an identifier can be used. All identifiers have scope.

  2.   Storage class is an area of memory. objects, temporary expresisons, variables, all have a storage class.

  3.   For globals, when the object module (library or executable) is loaded. For block-scope locals, they are initialized when the code is executed for the first time.

  4.   It turns global scope objects into file-scope objects (not exported by the linker). They are local to the source module only.

  5.   adding the keyword extern turns a definition into an external declaration. No storage is allocated, no initial value can be assigned. The object must be defined in another module. extern tells the compiler let the linker figure out where the definition is.

  6.   It changes a global name to file-scope, preventing it from being exported to the linker.

      When applied to a block scope object, it changes the storage class from stack to static.

Chapter 21: Memory Access review questions.

(Section 21.11)

  1.   p is an int*, while q is an int.

  2.   With pointers, those operators can only be used if the pointer holds the address of the first element of an array and provided that the results stay within the context of the array. The second operand of + and - must be an int n and the result of the operation will be an address equal to the original address + or - n times the size of the type of the "pointed-to" thing. The unary increment/decrement operators work similarly, but with a built-in second operand of 1.

  3.   The results are undefined - heap corruption is likely.

  4.   The address to the first element in the array.

Chapter 22: Inheritance in Detail review questions.

(Section 22.5)

  1.   It is a virtual jump table - it can be implemented as a list of pointers to all methods (virtual functions) of a class.

  2.   A class with at least one virtual function. A class which has a vtable.

  3.   They are copy constructors, copy assignment operators, and destructors. It is not appropriate to inherit these methods because they must perform operations on each member of the derived class, and the base class knows nothing about derived class types. Each of these functions are auto-generated for classes which do not supply one.

  4.   When operating on a collection of polymorphic types, as long as there is one method, there should be a virtual destructor.

  5.   Dynamic binding does not happen. The compiler will resolve the call at compile time, meaning that the derived override will never be called from a base class constructor.

  6.   virtual inheritance makes it possible to avoid multiply-inheriting the same class, when it appears as more than one base's base class.

  7.   It is very similar to having a non-public subobject, but it becomes possible to inherit methods that are only accessible in the class itself (and derived classes, if it is protected derivation)



[137] note the reference - const_cast works only on indirect types