How to Implement Our New string Functionality

There are a number of ways that I could have implemented the functionality that we will need to compare and search for character data without regard for the case of the text we are looking for or comparing. Here are several possibilities:

  1. Writing global functions to do case insensitive searches and comparisons.

  2. Using a completely separate string class like the one we created in Chapters 7 and 8.

  3. Using the standard library features that allow us to specify our own comparison and searching functions for the standard string class.

  4. Creating a new class that includes a standard library string member variable and forwarding all of the member functions implemented in the standard library string class to that member variable.

  5. Deriving a new class by public inheritance from the standard library string class.

I have decided to solve this problem by creating a new class based on the standard string class. I'm calling this new class xstring, for “extended string”. How did I come to this decision?

First of all, I don't like global functions, as they make it more difficult for other programmers to keep control of their namespaces. That is, every global function that we add to our programs has the possibility of interfering with another global function written by someone else who wants to use our classes. So I use global functions only when there is no other alternative available, or when I know that they cannot possibly interfere with other programmers' namespaces. For example, global functions used for input and output of our HomeItem objects should not be a problem, as anyone using our HomeItem classes must already have agreed to our definitions for those classes, and therefore should not have any namespace problems caused by the normal global I/O functions associated with those classes. However, of course, most programmers are going to use the standard library string class, and will not be amused if we introduce global functions that might interfere with their existing functions that use the standard library strings.

Okay, so how about switching back to our homegrown string class, defined in Chapters 7 and 8? I don't like that idea very much either, because, as I've already explained, we should get used to the idea of using the standard library as much as possible. Hundreds or maybe thousands of programmer-years have gone into the design and implementation of that library, and even if we don't care for some of its characteristics, we should use it whenever it is feasible to do so.

But if that is true, why did I reject the third alternative of using the built-in standard library facilities that allow us to specify our own searching and comparison criteria?

Because those facilities are so complex to set up and use. This is, after all, a book on learning how to program, not on the arcane details of the standard library. The material we would have to cover for you to be able to understand how to implement the “normal” method of specifying custom comparison and searching operations for the standard library string class is just too difficult for beginning programmers.

Another possibility is to create a new class that includes a standard library string as a member variable, and passing all of the operations implemented in the standard library string class to that member variable. This is not practical because there are so many member functions in the standard library string class. This approach would require us to write a large number of functions in our class just to forward all of those operations to our member variable.

So that leaves us with the option of extending the facilities provided by the standard string class by another method. I have chosen to create a new xstring class that is publicly derived from the standard library string class. Is this really a viable way to proceed?

Why Our New xstring class Is a Good Design Choice

I'm sure there will be objections to my decision to inherit publicly from the standard library string class. This class, like many of the classes in the standard library, was not specifically intended to be inherited from. And, in fact, if we were going to add data members to this class, our design would be flawed, because of the problem of “slicing”.

That is, if we were to add new data members to our derived class, and then accidentally assigned one of our derived class objects to a standard library string, the new data members would be lost during the assignment. This is one of the problems with inheriting from classes that were not intended specifically for such inheritance.

However, in this case, we will not run into this problem, because we're not adding any data members to our new class. All we're adding are two regular member functions that will allow us to do case-insensitive comparisons and searches. Therefore, assigning one of our new objects to a standard library string would not cause any data to be lost.

Another possible problem with deriving from the standard library string class is that the destructor for that class is not virtual. Therefore, if someone were to create a pointer to a std::string, assign an xstring to it, and then delete the xstring via that pointer, the destructor for std::string would be called rather than the destructor for xstring.

According to the C++ standard, deleting a derived class object through a base class pointer, where the base class destructor is not declared virtual, is “undefined behavior” (i.e., the compiler can do anything it wants).

However, I'm not worried about that problem, for two reasons: first, since xstring has no member variables, other than its base class part, I have some difficulty figuring out why a call to delete through a base class pointer would do anything other than destroying the string base class part, which is all we wanted to do anyway.

But more importantly, there's no reason for anyone ever to refer to an xstring via a pointer to a std::string because std::string doesn't have any virtual functions. This means that any function call through an xstring via a pointer to a std::string would always result in a call to the corresponding function in std::string. Therefore, no one should ever make the error of deleting an xstring through a pointer to a std::string.[2]

[2] I've suggested a change to the C++ standard that would add a new type of derivation called using inheritance. This new form of inheritance would allow convenient use of all of the facilities of the base class part of a derived class object without allowing a base class pointer to point to a derived class object. Maybe the next revision of the standard will provide this facility, in which case the last reasonable objection to deriving xstring from std::string will be removed.

I'm also following proper object-oriented design by creating a new class whose objects can be substituted for those of the original class without undue surprises to the user of these classes.

Given all of these considerations, I can't see any valid objection to our writing our own class that extends the functionality of the standard library string class.

The Interface of the New xstring class

Now that we've cleared that up, let's take a look at Figure 12.1, which shows the interface of the new xstring class that implements the additional string-related functions we'll need to finish our home inventory project.

Figure 12.1. The new xstring class interface (codexstring.h)
#ifndef XSTRING_H
#define XSTRING_H

#include <iostream>
#include <string>

class xstring : public std::string
{
public:
  xstring();
  xstring(const xstring& Str);
  xstring(const std::string& Str);
  xstring(char* p);
  xstring(unsigned Length, char Ch);
explicit xstring(unsigned Length);

  short find_nocase(const xstring& Str);
  bool less_nocase (const xstring& Str);
};

#endif

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

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