Class Polymorphism Example
Spring 2002
Table of Contents
1. Introduction
2. Source Code
Instructor Comments
Section 1. Introduction

This example demonstrates how polymorphism can be used to make it appear as if one container (e.g., vector, list etc...) can hold similar but fundamentally different objects. In a previous example we implemented the following classes: TextDoc, Folder, and SuperFolder. The first class represented a text document. The second class used a list data member to store multiple TextDoc objects. The third class inherited from the Folder class and added the ability to store Folder objects within it with an additional list data member. It should be noted that the SuperFolder class could store TextDoc and Folder objects, but it could not store SuperFolder objects. Therefore, we could not represent a directory structure that was more than three levels deep (a SuperFolder that contained a Folder that contained a TextDoc).

In this example we will

Concept 1: Inheritance to remove redundant code

In the previous example, we had very similar functions in the TextDoc and Folder classes. TextDoc::getDocName() was very similar to Folder::getFolderName(). The data members TextDoc::docName and Folder::fname were also very similar. In this example, we will avoid this redundancy by creating a base class, DesktopItem, that will describe characteristics that are common to all items that you may find on your computer desktop. The new versions of the TextDoc (TextFile) and Folder classes will inherit this functionality from the DesktopItem class.

There are other advantages for creating this base class, but it makes sense to use inheritance here even if we didn't make use of the other advantages.

Concept 2: Base class pointer to derived class object

Because derived class objects have some common characteristics with their parent class objects, it is possible for a base class pointer to point to a derived class object. This is useful because we can now create a list of pointers to DesktopItem objects as a data member in the Folder class. This data member will be able to "store" any kind of desktop item.

Not only does this allow us to avoid having to have to list data members in the Folder class, it also means that if we decide to define another specific type of desktop item, (e.g., an MP3File class), we will not need to modify the Folder class. It will automatically be able to keep track of MP3File objects (provided the MP3File class inherits the DesktopItem class).

Concept 3: Run-time binding

The true power of polymorphism is run-time binding (a.k.a., dynamic binding or late binding). The Folder class has a list of DesktopItem pointers as a data member. When the program is compiled, it is not clear what each DesktopItem* points to. (Consider the first DesktopItem* in the list. How do we know if it is pointing to a DesktopItem, a TextFile, or a Folder? It depends on what object was first appended to the Folder object, and we may not know this until the program is run.)

If we were to call getItemName (a member function from the DesktopItem class), the code implemented in the DesktopItem.cpp file will be called. That's just what we want because the function is not overridden in any of the derived classes. However, if we were to call size (another member function from the DesktopItem class), the code implemented in the DesktopItem.cpp file will be called. That's fine if the pointer is pointing to a DesktopItem object, but it it is pointing to a Folder object, it should be calling the overridden function from the Folder class instead (same goes for a TextFile object). The default in C++ is to call the DesktopItem class regardless of what the pointer is pointing to. This is known as static binding (a.k.a., compilte-time binding or early binding) because the decision of which function to call (or bind/link to) is done once and does not change.

Run-time binding allows us to delay the decision of which function to call until the program is running. This often referred to as dynamic binding because the decision of which function to bind to may change as the program is executed. This feature is enabled by use of the virtual keyword. In order to make use of dynamic binding, we must have a base class pointer that points to an object from a derived class (e.g., a DesktopItem* that points to a Folder object). By declaring a member function of the base class to be virtual, the binding of that function, when called by way of the base class pointer, will be delayed until run-time. At run-time, code will be run to determine the what type of object the pointer is pointing to and call the appropriate version of the function. Consider the following example:

      Folder aFolder("Example");
      DesktopItem* ptr=&aFolder;
      cout << ptr->getItemName();
      cout << ptr->size();

Here we created a Folder object and made a pointer, ptr, that points to it. We then call two member functions: getItemName() and size(). The first function is bound at compile time because it was not declared to be a virtual function. The second function is bound at run-time because it was declare as a virtual function. As a result, when this code is executed, the computer first looks to see what type of object ptr is pointing to and then calls the appropriate version of size(). Polymorphism (this concept) is what allows me to implement the Folder class with just one list.

It should also be noted that this design is much more flexible than the previous design. For example, if we were to create a MP3File class that described MP3 files, we could add MP3 files to my folder (provided that the MP3File class inherited from the the DesktopItem class) without modifying my Folder class at all.

Concept 4: Abstract Base Class

In the above discussion I have talked as if we could create DesktopItem objects. It just so happens that we cannot. The reason we can't create DesktopItem objects is because the DesktopItem class is an abstract base class.

Abstract base classes are useful for defining a functional interface for a family of classes that are derived from it. Typically the abstract base class defines a class that is so general that no objects from the base class actually exist. For example, an animal class would be an abstract base class. There are specific types of animals that may be found in the wild, but an animal, in and of itself, does not exist... it must be a specific type of animal. In this example, we will never create a DesktopItem; we will only create specific types of DesktopItems, like TextFiles and Folders. Therefore, it makes sense that the DesktopItem class is an abstract base class.

One way to figure out if a class should be an abstract base class is to determine if there is a member function that should be implemented in all of the derived classes that cannot be implemented in the base class. In this example, it doesn't make sense to implement the erase() function in the DesktopItem since there are no data members that can be erased. Therefore, we don't actually implement this function. Instead, we declare erase() to be a pure virtual function. A pure virtual function is declared like a virtual function with one addition: it ends with = 0;. This signifies that it is a pure virtual function and that it should not be implemented in the .cpp file.

Any class with at least one pure virtual function is an abstract base class. Objects cannot be created from this class since there is at least one member function that can't be called (since we didn't implemented it). Okay, so why create a class that can't have objects? Well, we can still create pointers to objects from the class (they just have to point to objects that are from classes derived from the abstract base class). Abstract base classes can be useful for defining a common interface for all the derived classes. In addition, they allow us to "fake" storing all of these different, but related, objects in one container. In our example, the abstract base class, DesktopItem is used to provide a common interface for TextFile and Folder objects and allows us to keep track of these different kinds of objects in one list.

Section 2. Source Code
DesktopItem.h
   1// DesktopItem.h
   2// Author: t a y l o r@msoe.edu and sections 6 of the MSOE Spring 2002 CS183 course
   3// Date: 4-18-2002
   4// Purpose: Defines a DesktopItem class which can be used as a base
   5//          for other classes that represent specific types of desktop
   6//          items.
   7
   8#ifndef DESKTOPITEM_H
   9#define DESKTOPITEM_H
  10
  11#include <string>
  12using std::string;
  13
  14class DesktopItem {
  15public:
  16  // Default constructor
  17  DesktopItem(const string& name);
  18
  19  // Copy constructor
  20  DesktopItem(const DesktopItem& old);
  21
  22  // Destructor
  23  virtual ~DesktopItem();

Destructors should always be declared as virtual to make sure that the appropriate destructor is called.

  24
  25  // Assignment operator
  26  virtual DesktopItem& operator=(const DesktopItem& rhs);
  27
  28  // Changes the name of the desktop item
  29  void rename(const string& name);
  30
  31  // Returns the name of the desktop item
  32  const string& getItemName() const;
  33
  34  // Returns the size of the desktop item (in bytes)... overridden in
  35  //  derived classes
  36  virtual unsigned int size() const;
  37
  38  // Erases the connents of the desktop item... must be overridden in
  39  //  derived classes
  40  virtual void erase() = 0;                // Pure virtual function

Since it really doesn't make sense to implement this function, we could declare it as a pure virtual function as follows:

       virtual unsigned int getSizeInBytes() const = 0;

A pure virtual function should not be implemented. It serves to provide an interface for derived classes that would share this common function. The derived classes would then be required to implement this function.

One consequence of declaring a member function as pure virtual is that it now doesn't make sense to have objects from this class (since at least one of their member functions has not been implemented). In fact, it is not possible to create objects from a class that contains at least one pure virtual member function. Such a class is called an abstract base class.

  41
  42  // Dynamically allocates a copy of the desktop item and returns a pointer
  43  //  to the new item... must be overridden in derived classes
  44  virtual DesktopItem* clone() const = 0;  // Pure virtual function
  45protected:
  46  string itemName;
  47private:
  48  // Default constructor hidden from use
  49  DesktopItem();
  50};
  51
  52#endif
TextFile.h
   1// TextFile.h
   2// Author: t a y l o r@msoe.edu and sections 6 of the MSOE Spring 2002 CS183 course
   3// Date: 4-18-2002
   4// Purpose: Defines a TextFile class which represents a text document.
   5
   6#ifndef TEXTFILE_H
   7#define TEXTFILE_H
   8
   9#include <string>
  10#include "DesktopItem.h"
  11using std::string;
  12
  13class TextFile : public DesktopItem {
  14public:
  15  // Default constructor
  16  TextFile(const string& name);
  17
  18  // Copy constructor
  19  TextFile(const TextFile& rhs);
  20
  21  // Destructor
  22  ~TextFile();
  23
  24  // Assignment operator
  25  TextFile& operator=(const TextFile& rhs);
  26
  27  // Returns a const reference to the text contained in the TextFile object
  28  const string& contents() const;
  29
  30  // Returns a reference to the text contained in the TextFile object
  31  string& contents();
  32
  33  // Returns the number of the bytes required to store the text contained
  34  //  in the TextFile object
  35  unsigned int size() const;
  36
  37  // Erases the text in the TextFile object
  38  void erase();
  39
  40  // Make a dynamic copy of the current object and return a pointer to
  41  //  the newly created object
  42  DesktopItem* clone() const;
  43
  44protected:
  45  string text;
  46
  47private:
  48  // Default constructor has been hidden
  49  TextFile();
  50};
  51
  52#endif
Folder.h
   1// Folder.h
   2// Author: t a y l o r@msoe.edu and sections 6 of the MSOE Spring 2002 CS183 course
   3// Date: 4-18-2002
   4// Purpose: Defines a Folder class which represents a file system folder
   5//          that stores desktop items (pointers to DesktopItem objects).
   6
   7#ifndef FOLDER_H
   8#define FOLDER_H
   9
  10#include <string>
  11#include <list>
  12#include "DesktopItem.h"
  13
  14using std::string;
  15using std::list;
  16
  17class Folder : public DesktopItem {
  18public:
  19  // Default constructor
  20  Folder(const string& name);
  21
  22  // Copy constructor
  23  Folder(const Folder& rhs);
  24
  25  // Destructor
  26  ~Folder();
  27
  28  // Assignment operator
  29  Folder& operator=(const Folder& rhs);
  30
  31  // Searches for a desktop item with the same name as item.  If none exists,
  32  //  item is added to the list of desktop items and true is returned. 
  33  //  Otherwise, it returns false.
  34  bool add(const DesktopItem* item);

By passing in a pointer to a DesktopItem we can in effect pass any type of object whose class inherits the DesktopItem class.

  35
  36  // Searches for a desktop item called name.  If one is found, it removes
  37  //  it from the SuperFolder object and returns true.  Otherwise, it
  38  //  returns false.
  39  bool remove(const string& name);
  40
  41  // Searches for a desktop item called name.  If one is found, it returns
  42  //  a pointer to it.  Otherwise it returns null pointer.
  43  DesktopItem* getItem(const string& name);
  44
  45  // Returns the number of bytes used by all of the items in the folder
  46  unsigned int size() const;
  47
  48  // Removes all desktop items from the folder object
  49  void erase();
  50
  51  // Make a dynamic copy of the current object and return a pointer to
  52  //  the newly created object
  53  DesktopItem* clone() const;
  54protected:
  55  // Adds the items in the list passed in to the list in the current object
  56  void add(const list<DesktopItem*>& items);

The protected member function add() that takes a list of pointers will be used by a number of other member functions. By creating this member function we eliminate duplicate code that would have existed in different member functions. For example, both the copy constructor and the assignment operator add items to the current object. Rather than writing the same code in each member function, we will call the add() function. This makes our implementation easier to read and maintain (changes only need to be made once). They are declared as protected member functions since they are designed to be used only by other member fuctions of the Folder class (and perhaps class that inherit the Folder class).

  57
  58  list<DesktopItem*> desktopItems;

Here we have just one list to keep track of all different kinds of desktop items. Since a DesktopItem* can point to DesktopItem, TextFile, and Folder objects, we can keep track of all of these in one list.

Even more significantly, if we were to create additional classes that describe other kinds of desktop items (e.g., XMLFile, MSWordFile, MP3File, MSExcelFile, LaserPrinter, etc...), they could all be added to a Folder object without needing to modify the Folder class implementation at all! (This assumes that each of these additional classes inherits the DesktopItem class.)

  59private:
  60  // Default constructor has been hidden
  61  Folder();
  62};
  63
  64#endif
DesktopItem.cpp
   1// DesktopItem.cpp
   2// Author: t a y l o r@msoe.edu
   3// Date: 4-19-2002
   4// Purpose: Defines a DesktopItem class which can be used as a base
   5//          for other classes that represent specific types of desktop
   6//          items.
   7
   8#include <cassert>
   9#include "DesktopItem.h"
  10
  11// t a y l o r@msoe.edu, 4-19-2002
  12DesktopItem::DesktopItem(const string& name) : itemName(name)
  13{
  14  // Nothing else to do
  15}
  16
  17// t a y l o r@msoe.edu, 4-19-2002
  18DesktopItem::DesktopItem(const DesktopItem& old) : itemName(old.itemName)
  19{
  20  // Nothing else to do
  21}
  22
  23// t a y l o r@msoe.edu, 4-19-2002
  24DesktopItem::~DesktopItem()
  25{
  26  // Nothing to do
  27}
  28
  29// t a y l o r@msoe.edu, 4-19-2002
  30DesktopItem& DesktopItem::operator=(const DesktopItem& rhs)
  31{
  32  if(this!=&rhs) {
  33    itemName = rhs.itemName;
  34  }
  35  return *this;
  36}
  37
  38// t a y l o r@msoe.edu, 4-19-2002
  39void DesktopItem::rename(const string& name)
  40{
  41  itemName = name;
  42}
  43
  44// t a y l o r@msoe.edu, 4-19-2002
  45const string& DesktopItem::getItemName() const
  46{
  47  return itemName;
  48}
  49
  50// t a y l o r@msoe.edu, 4-19-2002
  51unsigned int DesktopItem::size() const
  52{
  53  return itemName.size();
  54}
  55
  56// t a y l o r@msoe.edu, 4-19-2002
  57DesktopItem::DesktopItem()
  58{
  59  assert(false);
  60}
TextFile.cpp
   1// TextFile.cpp
   2// Author: t a y l o r@msoe.edu and sections 2 and 6 of the
   3//         MSOE Spring 2001 CS183 course
   4// Date: 4-19-2002
   5// Purpose: Defines a TextFile class which represents a text document.
   6
   7#include <string>
   8#include <cassert>
   9#include "TextFile.h"
  10
  11using std::string;
  12
  13// t a y l o r@msoe.edu, 4-19-2002
  14TextFile::TextFile(const string& name) : DesktopItem(name)

This one seemed to give some trouble to my students, so let's step back a bit before we try to tackle this. Let's first consider the constructor for the DesktopItem class. When a DesktopItem object is instantiated (supposing one could be), the constructor is called. Each data member in the initializer list is created by passing the arguments given in the initializer list to the constructor for the object's class. Each data member that is not in the initializer list is created using its default constructor. Once all of the data members have been created, the body of the constructor is executed.

Now let's consider the behavior for a constructor of a derived class (like TextFile). In addition to initializing all of the data members, all of the data members inherited from the base class are created by calling the constructor from the base class. If the base class constructor is explicitly listed in the initializer list, the constructor is called with the given parameters. Otherwise, the default constructor for the base class is called in order to instantiate all of the data members that were inherited from the base class.

In this example, we call the constructor for the DesktopItem class explicitly in the initializer list. This sets the itemName data member to the value of name. It is worth noting that we need to make this call explicitly. Can you see what would go wrong if we didn't call it this way?

  15{
  16  // Nothing else to do
  17}
  18
  19// t a y l o r@msoe.edu, 4-19-2002
  20TextFile::TextFile(const TextFile& rhs) : DesktopItem(rhs),

In the previous constructor, the initializer list called the DesktopItem::DesktopItem(const string& name) constructor. In this initializer list we are calling the DesktopItem::DesktopItem(const DesktopItem& old) constructor. The interesting thing here is that while the constructor takes a reference to a DesktopItem, we are really passing a reference to a TextFile.

Generally speaking, we have forced a derived class object to "look like" a base class object. This is known as upcasting, and it is perfectly legal since the base class knows (at least partially) what the derived class object can do. However, the base class only knows about the stuff in the derived class that was inherited from the base class. Therefore, any additional members (data or functions) that were defined in the derived class are not available to old in the DesktopItem constructor. This loss of data is known as object slicing. In this case, the fact that we have lost data associated with rhs when passing to the DesktopItem constructor should not concern us because we are relying on the DesktopItem constructor only to take care of the data members inherited from the DesktopItem class. The second item in the initializer list takes care of copying the text data member.

Before we move on, let's consider the following alternate implementation of the copy constructor:

     TextFile::TextFile(const TextFile& rhs) : text(rhs.text)
     {
       itemName = rhs.itemName;
     }

In this case we do not explicitly call the DesktopItem copy constructor, instead, the default constructor from the DesktopItem class is implicitly called before the TextFile constructor is executed. As a result, we need to set the value of the itemName data member within the body of the TextFile constructor. It turns out that this implementation is not legal for the same reason we had to call the DesktopItem constructor in the previous constructor implementation. Did you figure out why it wouldn't work above? In both cases we have to have an explicit call to a DesktopItem constructor because the default constructor for the DesktopItem class is private (and therefore, not available to the TextFile class). It should also be noted that placing itemName(rhs.itemName) in the initializer list will cause a compile error. Why do you think this results in an error?

  21         text(rhs.text)
  22{
  23  // Nothing else to do
  24}
  25
  26// t a y l o r@msoe.edu, 4-19-2002
  27TextFile::~TextFile()
  28{
  29  // Nothing to do
  30}

Just as the DesktopItem constructor was implicitly called in the alternate implementation of the copy constructor, the DesktopItem destructor is implicitly called. The destructor for the parent class is called immediately following the completion of the destructor from the derived class.

  31
  32// t a y l o r@msoe.edu, 4-19-2002
  33TextFile& TextFile::operator=(const TextFile& rhs)
  34{
  35  if(this!=&rhs) {
  36    DesktopItem::operator=(rhs);
  37    text = rhs.text;
  38  }
  39  return *this;
  40}

It is worth noting that we have made an explicit call to the assignment operator from the DesktopItem class. We did not need to do it this way. In fact, it may have been easier to just say itemName = rhs.itemName; instead. However, making explicit use of the functions from the base class (both here and in the constructors) means that if the DesktopItem class is enhanced to include additional data members/member functions (e.g., we could add a data member to keep track of the time the file was created) the TextFile constructors and assignment operator would not require modification because we are relying on the member functions from the DesktopItem to handle any data members associated with the DesktopItem class.

  41
  42// t a y l o r@msoe.edu, 4-19-2002
  43const string& TextFile::contents() const
  44{
  45  return text;
  46}
  47
  48// t a y l o r@msoe.edu, 4-19-2002
  49string& TextFile::contents()
  50{
  51  return text;
  52}
  53
  54// t a y l o r@msoe.edu, 4-19-2002
  55unsigned int TextFile::size() const
  56{
  57  return DesktopItem::size() + text.size();
  58}
  59
  60// t a y l o r@msoe.edu, 4-19-2002
  61void TextFile::erase()
  62{
  63  text = "";
  64}
  65
  66// t a y l o r@msoe.edu, 4-19-2002
  67DesktopItem* TextFile::clone() const
  68{
  69  return new TextFile(*this);
  70}

The clone function dynamically allocates a copy of the calling TextFile object and returns a pointer to it. See more discussion on how this function is useful in the Folder class.

Why do you suppose this function was declared a pure virtual function in the DesktopItem class? Hint: It was declared pure virtual because it is not possible to implement it in the DesktopItem class. Why couldn't we implement it in the DesktopItem class?

  71
  72// t a y l o r@msoe.edu, 4-19-2002
  73TextFile::TextFile() : DesktopItem("")
  74{
  75  assert(false);
  76}
Folder.cpp
   1// Folder.cpp
   2// Author: t a y l o r@msoe.edu
   3// Date: 4-19-2002
   4// Purpose: Defines a Folder class which represents a file system folder
   5//          that stores desktop items (pointers to DesktopItem objects).
   6
   7#include <string>
   8#include <list>
   9#include <iostream>
  10#include <cassert>
  11#include "Folder.h"
  12using std::cerr;
  13
  14using std::string;
  15using std::list;
  16
  17// t a y l o r@msoe.edu, 4-19-2002
  18Folder::Folder(const string& name) : DesktopItem(name)
  19{
  20  // Nothing else to do
  21}
  22
  23// t a y l o r@msoe.edu, 4-19-2002
  24Folder::Folder(const Folder& rhs) : DesktopItem(rhs)
  25{
  26  // Make a deep copy of all of the items in the list of items
  27  add(rhs.desktopItems);
  28}

As with the TextFile copy constructor, we explicitly call the DesktopItem copy constructor to handle all of the data members associated with the DesktopItem class (itemName). In addition, we need to make a copy of all of the items in the list of desktop items from rhs. Since desktopItems is a list of pointers, we need to do more than just copy the list (copying the list would just give us copies of the pointers, but the pointers would be pointing to exactly the same TextFile and Folder objects that the original list pointed to. Instead, we want to make a copy of each object and add a pointer to the new list that points to the newly created object. This is all done by the protected add() member function.

  29
  30// t a y l o r@msoe.edu, 4-19-2002
  31Folder::~Folder()
  32{
  33  erase();
  34}

We actually have something to do in the destructor this time. Remember that the desktopItems data member is a list of pointers. The objects that the elements of the list point to are dynamically allocated using the new operator. Therefore, we need to delete them before we destroy the list of pointers. (If we did not do this, we would have a massive memory leak because each object pointed to by an element of the list would now be lost, but the memory associated with the object would not be recovered.) All of this work is done in the erase() member function.

  35
  36// t a y l o r@msoe.edu, 4-19-2002
  37Folder& Folder::operator=(const Folder& rhs)
  38{
  39  if(this!=&rhs) {
  40    DesktopItem::operator=(rhs);
  41    erase();
  42    add(rhs.desktopItems);
  43  }
  44  return *this;
  45}

Here it is critical that we check of self-assignment (A=A). The reason this is critical is that if we did not, all the items in the folder would be lost anytime a folder was assigned to itself. Can you see why?

Much like the assignment operator of the TextFile class, we make use of the assignment operator from the DesktopItem class to take care of the data members inherited from the DesktopItem class. We then erase the old list (deallocating the memory for each object to avoid memory leaks), and then add the elements from the list in rhs.

  46
  47// t a y l o r@msoe.edu, 4-19-2002
  48bool Folder::add(const DesktopItem* item)
  49{
  50  bool found = false;
  51  // Iterate through the list until itemNames match (or we get to the
  52  //  end of the list)
  53  std::list<DesktopItem*>::const_iterator itr = desktopItems.begin();
  54  while(!found && itr!=desktopItems.end()) {
  55    if(item->getItemName() == (*itr)->getItemName()) {
  56      found = true;
  57    } else {
  58      ++itr;
  59    }
  60  }

This function loops through all of the elements in the folder (the list of DesktopItem* called desktopItems) until it finds an item with a matching name or reaches the end of the list. If no match was found, the if statement below is executed. It calls the appropriate version of the clone member function (using polymorphism, it looks to see what type of object item is pointing to and calls the version of the function from that class. It then pushs the pointer to the newly created object onto the end of the list.

  61  // Since no match was found, create a copy of the object that item is
  62  //  pointing to and a pointer to it to the desktopItems list
  63  if(!found) {
  64    desktopItems.push_back(item->clone());
  65  }
  66  return !found;
  67}
  68
  69// t a y l o r@msoe.edu, 4-19-2002
  70bool Folder::remove(const string& name)
  71{
  72  bool found = false;
  73  std::list<DesktopItem*>::iterator itr = desktopItems.begin();
  74  // Look for a match
  75  while(!found && itr!=desktopItems.end()) {
  76    // If a match is found, deallocate memory holding the object and
  77    //  remove the pointer to it from the desktopItem list
  78    if(name == (*itr)->getItemName()) {
  79      delete *itr;
  80      desktopItems.erase(itr);
  81      found = true;
  82    } else {
  83      ++itr;
  84    }
  85  }
  86  return found;
  87}

If an item in the folder has an itemName that matches name, the item is removed from the folder. This is done by first deallocating the memory associated with the object and then removing the pointer to it from the list.

  88
  89// t a y l o r@msoe.edu, 4-19-2002
  90DesktopItem* Folder::getItem(const string& name)
  91{
  92  DesktopItem* retVal=0;
  93  std::list<DesktopItem*>::const_iterator itr = desktopItems.begin();
  94  while(itr!=desktopItems.end() && name != (*itr)->getItemName()) {
  95    ++itr;
  96  }
  97  if(itr!=desktopItems.end()) {
  98    retVal = *itr;
  99  }
 100  return retVal;
 101}
 102
 103// t a y l o r@msoe.edu, 4-19-2002
 104unsigned int Folder::size() const
 105{
 106  unsigned int sz = DesktopItem::size();
 107  std::list<DesktopItem*>::const_iterator itr = desktopItems.begin();
 108  // Iterate through the list of desktop items and add the size of
 109  //  each item to the total
 110  while(itr!=desktopItems.end()) {
 111    sz += (*itr)->size();
 112    ++itr;
 113  }
 114  return sz;
 115}

What does this do if we have a folder of 9 folders where each folder is 1 MB big and a text file that is 1 MB big? Does it return a size of approximately 1 MB or approximately 10 MB? How do you know?

 116
 117// t a y l o r@msoe.edu, 4-19-2002
 118void Folder::erase()
 119{
 120  std::list<DesktopItem*>::iterator itr = desktopItems.begin();
 121  // Free the memory where each object is actually stored.
 122  while(itr!=desktopItems.end()) {
 123    delete *itr;
 124    ++itr;
 125  }
 126  // Get rid of all the pointers in the desktopItems list
 127  desktopItems.clear();
 128}
 129
 130// t a y l o r@msoe.edu, 4-19-2002
 131DesktopItem* Folder::clone() const
 132{
 133  return new Folder(*this);
 134}
 135
 136// t a y l o r@msoe.edu, 4-19-2002
 137void Folder::add(const list<DesktopItem*>& newItems)
 138{
 139  std::list<DesktopItem*>::const_iterator itr = newItems.begin();
 140  while(itr!=newItems.end()) {
 141    desktopItems.push_back((*itr)->clone());
 142    ++itr;
 143  }
 144}
 145
 146// t a y l o r@msoe.edu, 4-19-2002
 147Folder::Folder() : DesktopItem("")
 148{
 149  assert(false);
 150}
Copyright   2001 Dr. Christopher C. Taylor t a y l o r@m s o e.e d u Last updated: Mon Apr 22 17:10:34 2002