The New Member Functions of HomeItemMusic

We've now covered all the new member functions in HomeItemBasic, so it's time to take a look at the functions implemented in HomeItemMusic. Thankfully, there aren't as many of these. For one thing, GetName, Read, and Edit don't have to be overridden in HomeItemMusic because they do most of their work by calling virtual functions anyway. Two more functions in this class, Write and GetType, haven't changed since we first saw them in hmit1.cpp, so we don't have to go over them again. HomeItemMusic::FormattedDisplay, on the other hand, has changed since we examined it in hmit4.cpp, so it would be a good idea to look it over quickly (Figure 11.42).

Figure 11.42. HomeItemMusic::FormattedDisplay (from codehmit5.cpp)
short HomeItemMusic::FormattedDisplay(ostream &os)
{
 short FieldNumber = HomeItemBasic::FormattedDisplay(os);

 short TrackCount = m_Track.size();

 os << FieldNumber << ". ";
 os << GetFieldName(FieldNumber) << ": ";
 FieldNumber ++;
 os << m_Artist << endl;

 os << FieldNumber << ". ";
 os << GetFieldName(FieldNumber) << ": ";
 FieldNumber ++;
 os << TrackCount << endl;

 for (short i = 0; i < TrackCount; i ++)
  {
  os << FieldNumber << ". ";
  os << GetFieldName(FieldNumber) << i + 1 << ": ";
  FieldNumber ++;
  os << m_Track[i] << endl;
  }

 return FieldNumber;
}

This function isn't very different from its counterpart in HomeItemBasic, but there are a couple of points worth mentioning. First, of course, it calls HomeItemBasic::FormattedDisplay to display the part of the data contained in the base class part of the object. Then it assigns the return value from that call to a local variable called FieldNumber, which it uses to keep track of the current field it is displaying. Why do we want to use the return value from that function to initialize our current field variable rather than a const value, as we did in the base class?

Keeping Track of the Next Field Number

We do this to reduce the difficulty of maintaining this program. If we use the return value from HomeItemBasic::FormattedDisplay, we won't have to make any changes in the derived class function if the HomeItemBasic class eventually has more data added to it; the starting field number in HomeItemMusic:FormattedDisplay will automatically be the right value as long as the modifications to the HomeItemBasic::FormattedDisplay function have been made correctly. Therefore, we have to make such a change only in one place rather than in two, as we would if we used a const value in the derived class function.

Susan wanted a bit more detail on this issue.

Susan: What kind of change would you make that might mess up the field numbers?

Steve: Let's suppose that we had six fields in the HomeItemBasic class, which of course would be numbered 1 through 6. In that case, the added fields in HomeItemMusic would start at 7. However, if we added another field to the HomeItemBasic class, then the number of the first field in the HomeItemMusic class would change to 8. All of this would have to be handled manually if we used a constant value to specify where we wanted to start in the HomeItemMusic class. However, as long as we use the return value from the HomeItemBasic version of FormattedDisplay, any such adjustments will happen automatically.

Susan: But the user might get confused if the field that used to be #5 suddenly became #6.

Steve: True, but there isn't much we can do about that, assuming that the new field was really necessary. All we can do is make sure that the numbers will still be in the right order with no gaps.

I should also mention that the field name prompt for track names is “Track #” followed by the track number. Because we don't want to confuse the user by starting at 0, we add 1 to the value of the loop index before we use it to construct the field name prompt. This ensures that the first track number displayed is 1, not 0. Remember, users don't normally count from 0, and we should humor them; without them, we wouldn't have anyone to use our programs!

Now let's take a look at HomeItemMusic::ReadInteractive (Figure 11.43).

Figure 11.43. HomeItemMusic::ReadInteractive (from codehmit5.cpp)
short HomeItemMusic::ReadInteractive()
{
 short TrackCount;

 short FieldNumber = HomeItemBasic::ReadInteractive();

 cout << FieldNumber << ". ";
 cout << GetFieldName(FieldNumber) << ": ";
 FieldNumber ++;
 getline(cin,m_Artist);

 cout << FieldNumber << ". ";
 cout << GetFieldName(FieldNumber) << ": ";
 FieldNumber ++;
 cin >> TrackCount;
 m_Track.resize(TrackCount);

 for (short i = 0; i < TrackCount; i ++)
  {
  cout << FieldNumber << ". ";
  cout << GetFieldName(FieldNumber) << i + 1 << ": ";
  FieldNumber ++;
  getline(cin,m_Track[i]);
  }

 return FieldNumber;
}

There are two more functions that we need to look at briefly. The first is HomeItemMusic::ReadFromFile, which is shown in Figure 11.44. Assuming that you understand the HomeItemBasic versions of ReadFromFile and ReadInteractive, this function should hold no secrets for you, so just take a look at it and make sure you understand it. Then let's move on.

Figure 11.44. HomeItemMusic::ReadFromFile (from codehmit5.cpp)
short HomeItemMusic::ReadFromFile(istream& is)
{
 short TrackCount;

 HomeItemBasic::ReadFromFile(is);

 getline(is,m_Artist);
 is >> TrackCount;
 is.ignore();

 m_Track.resize(TrackCount);
 for (short i = 0; i < TrackCount; i ++)
  {
  getline(is,m_Track[i]);
  }

 return 0;
}

Finally, there's HomeItemMusic::EditField (Figure 11.45), which has a few points that we should consider before (finally!) ending this chapter.

Figure 11.45. HomeItemMusic::EditField (from codehmit5.cpp)
bool HomeItemMusic::EditField(short FieldNumber)
{
 if (FieldNumber < e_Artist)
  {
  return HomeItemBasic::EditField(FieldNumber);
  }

 short TrackCount = m_Track.size();

 switch (FieldNumber)
  {
  case e_Artist:
  cout << FieldNumber << ". ";
  cout << GetFieldName(FieldNumber) << ": ";
  getline(cin,m_Artist);
  return true;

  case e_TrackCount:
  cout << FieldNumber << ". ";
  cout << GetFieldName(FieldNumber) << ": ";
  cin >> TrackCount;
  m_Track.resize(TrackCount);
  return true;
  }

 if (FieldNumber > (e_TrackCount + TrackCount))
  {
  cout << "Sorry, that is not a valid field number." << endl;
  return false;
  }

 cout << FieldNumber << ". ";
 cout << GetFieldName(FieldNumber);
 cout << FieldNumber - e_TrackCount << ": ";

 getline(cin,m_Track[FieldNumber - e_TrackNumber]);

 return true;
}

Let's start at the beginning, where we figure out whether the field the user wants to edit is handled by this function or by HomeItemBasic::Edit. If the field number is less than e_Artist, we know that this function isn't responsible for editing it, so we pass the editing task on to the HomeItemBasic version of EditField and return the return value from that function to our caller.

But suppose we have to handle the editing chore for the user's field here. In that case, we need to execute the proper code for the field the user wants to edit. If that field happens to be either the artist's name or the number of tracks, we handle it in the switch statement and return the value true to indicate success. However, handling the other fields (i.e., the track names) isn't quite as simple. If you compare HomeItemBasic::EditField (Figure 11.41) with the current function you'll notice that the switch statement in HomeItemBasic::EditField has a default case to handle the possibility that the field number is invalid, whereas the HomeItemMusic switch statement doesn't. Why is this?

Because a HomeItemMusic object can contain a variable number of track names in its m_Track member variable. This means that we can't tell at compile time how many fields are in the object we're going to edit, which in turn means that we have to wait until run time to figure out whether a particular field number is valid for a particular object. That's the purpose of the if statement

if (FieldNumber > (e_TrackCount + TrackCount))

that adds the number of tracks to the field number for the track count itself and then compares the result to the field number the user typed in. Since the track name fields immediately follow the track count field, if the user's field number is greater than that total, the field number is invalid. For example, if there's only one track, the maximum field number is e_TrackCount + 1; if there are two tracks, it is e_TrackCount + 2; and so on. If the field number the user typed is beyond the legal range, the code in the if statement displays a warning and returns the value false to the calling function to indicate that it was unable to update the object.

Assuming that the user typed in a legal field number, we continue with the code that prompts the user to type in the new name for the selected track:

cout << FieldNumber << ". ";
cout << GetFieldName(FieldNumber);
cout << FieldNumber - e_TrackCount << ": ";

This starts out by displaying the field number for the track to be edited followed by its field name (“Track #” followed by its track number). For example, if the starting field number for the track names is 8, the prompt for track 5 is “12. Track #5: ”. Then the line

getline(cin,m_Track[FieldNumber - e_TrackNumber]);

accepts the new value of the track name and stores it in the correct place in the m_Track member variable. Finally, the function returns true to indicate success.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset