8

I'm working on one of the programming challenges in the book Starting Out With C++ Early Objects 7th Edition and one of the assignments asks to create a class which is derived from the STL string class. I'm posting the question for the purpose of understanding what I am allowed to do and how I am supposed to implement the solution so that no one offers more advanced suggestions.

-- Question as it is written in the text --

Palindrome Testing

A Palindrome is a string that reads the same backward as forward. For example, the words mom, dad, madam, and radar are palindromes. Write a class Pstring that is derived from the STL string class. The Pstring class adds a member function

bool isPalindrome()

that determines whether the string is a palindrome. Include a constructor that takes an STL string object as a parameter and passes it to the string base class constructor. Test your class by having a main program that asks the user to enter a string. The program uses the string to initialize a Pstring object and then calls isPalindrome() to determine whether the string entered is a palindrome.

You may find it useful to use the subscript operator [] of the string class: if str is a string object and k is an integer, then str[k] returns the caracter at the position k in the string.

-- End --

My main question is how do I access the member variable which holds my string object if the class I am deriving Pstring from is a class I did not write and I do not know how it implements its members?

For example,

#include <string>
using namespace std;

class Pstring : public string
{
public:
  Pstring(std::string text)
   : string(text) { }

  bool isPalindrome()
  {
    // How do I access the string if I am passing it to the base class?

    // What I think I should do is...
    bool is_palindrome = true;
    auto iBegin = begin();
    auto iEnd   = end() - 1;

    while (iBegin < iEnd && is_palindrome)
    {
      if (*iBegin++ != *iEnd--)
        is_palindrome = false;
    }

    return is_palindrome;

    // But I think this is wrong because...
    // #1 The book did not discuss the keyword auto yet
    // #2 The book discussed when a class is derived from another class,
    //    how the members from super class will be accessible to the sub class.
    //    However, with this assignment, I don't see how to access the members.
  }
}

The reason I feel like I am doing this incorrectly is because the assignment mentions using subscript notation, however, I don't understand how to use the subscript notation if I don't know the name of the variable where the string is stored.

Any help would be greatly appreciated because the author does not provide the solutions unless I am an instructor which is pretty lame in my opinion. It probably has to do with the fact that this is an academic text.

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
  • 6
    Never derive from stl classes. Always a bad idea. Why don't you try composition instead? –  Dec 24 '11 at 04:41
  • 5
    The author may not provide solutions because he is an idiot. – Duck Dec 24 '11 at 04:41
  • @Ethan Steinberg I completely agree, however, this assignment specifically asks to do that. –  Dec 24 '11 at 04:42
  • 2
    In a perfect world, the author of this stupid idea is already inundated with emails asking how to learn the implementation details of every bloody implementation of STL strings on the planet and _hates_ the fact that he published such a horrible idea. Maybe he put together a FAQ on his web site with internal implementation details for the popular platforms as an act of contrition? – sarnold Dec 24 '11 at 04:58
  • 2
    Could someone explain **why** it's a bad idea to derive from STL classes instead of merely claiming the fact? Is private inheritance just as bad of an idea as public inheritance? I'm seeing an overreaction but with no explanation, and it might be useful to mention what the reason behind it is. – user541686 Dec 24 '11 at 05:09
  • You seem to have a little understanding problem about inheritance. Base class typedefs, like `iterator`, `const_iterator`, etc., are just inherited like base class member functions (`begin` and `end` for example, which you use). So just do `iterator = begin();`. – Xeo Dec 24 '11 at 05:13
  • 3
    @Mehrdad: STL containers and the string class aren't meant to be derived. They offer no virtual destructor which would let your derived class clean up its resources, they offer no `protected` access to any members. Also note that `private` inheritance is worlds apart from `public` inheritance. `private` inheritance is more like "implemented in terms of", while `public` inheritance is "is a". – Xeo Dec 24 '11 at 05:14
  • 7
    "I'm working on one of the programming challenges in the book Starting Out With C++ Early Objects 7th Edition and one of the assignments asks to create a class which is derived from the STL string class." Note to self: never purchase this book, read it, or do anything with it except use it as a door stopper and/or burn it. – Nicol Bolas Dec 24 '11 at 05:17
  • @Xeo: Right, so the fact that they offer no virtual destructor simply means you shouldn't inherit from them *publicly* (to avoid the user deleting a pointer to the base class). Or to just make sure that you never *expose* a publicly derived instance to the outside world, in case they decide to delete it via the base class. But saying "don't inherit from STL containers" is simply misleading because it also excludes private inheritance and it also excludes inheritances internal to your library, both of which are under your control and which can be safely used in different situations. – user541686 Dec 24 '11 at 05:17
  • 2
    @Mehrdad: There are also better alternatives to `private` inheritance, like free functions to augment the containers interface. You can also just use composition and forward to the members, but if you really only forward and do nothing useful, free functions are way superior (even `private` inheritance would be). – Xeo Dec 24 '11 at 05:21
  • @NicolBolas I LOLd in real life. (= I totally agree, this book was a waste of $130.00. I could go on a rant about all the short comings of this book and the very poor examples it offers. The book never even discusses bitwise operators among other things and this is a book centered around the C language. –  Dec 24 '11 at 05:23
  • 3
    @fhaddad: Wait, $130? That's pretty expensive fuel. – Xeo Dec 24 '11 at 05:24
  • 3
    @Mehrdad - All of that is fine. But this is a textbook for beginners. It's inexcusable in that context. – Duck Dec 24 '11 at 05:26
  • @Xeo: Yup. http://www.pearsonhighered.com/educator/product/Starting-Out-with-C-Early-Objects-7E/9780136077749.page. –  Dec 24 '11 at 05:28
  • @Duck: Yeah, I was kind of hoping to avoid the topic of how deriving from the STL string class is bad. That's why at the very beginning of my post I tried to make a mention about sticking to what the assignment asks for in the implementation and keeping all the more advanced and real-world implementations out of the discussion for the sake of simplicity. –  Dec 24 '11 at 05:30
  • 3
    @Mehrdad: It may be slightly misleading, but it's still solid advice. Because if you have to be *told* not to inherit from STL classes, you probably don't know enough about inheritance to even know that private inheritance exists. Also, what Xeo said; there's absolutely no reason for this "Palindrome" function to be a member of a string class. That's what free functions are *for*. – Nicol Bolas Dec 24 '11 at 05:32
  • What is the point of `Pstring`? what abstraction does it represent? – curiousguy Dec 24 '11 at 07:14

4 Answers4

3

You shouldn't inherit from std::string, as it wasn't designed for that, nor do you need to in order to find a palindrome.

See this: Inheriting and overriding functions of a std::string?

Palindrome solution (from this question: Check if a string is palindrome Linked from this: C++ Palindrome finder optimization)

#include <algorithm>

bool isPal(const string& testing) {
    return std::equal(testing.begin(), testing.begin() + testing.size() / 2, testing.rbegin());
}

That book's quality seems questionable. Free functions (depending on who you ask) are almost always preferred over member functions, and especially preferred over inheritance.


If you must use inheritance:

class Pstring : public string
{
    //...

    bool isPalindrome()
    {
      return std::equal(begin(), begin() + size() / 2, rbegin());

      // as a side-note, 'iterator' will refer to the inherited return type of begin()
      // Also, 'operator[](x)' will call the subscript operator
    }
};
Community
  • 1
  • 1
Pubby
  • 51,882
  • 13
  • 139
  • 180
  • Thank you for the response. I am aware that this is not good practice. However, the assignment specifically asks me to implement my solution the way I am attempting to above. By creating a class which is derived from the STL string class and then to create a function for testing if a string is a palindrome. I am trying to complete the assignment. I can not use any more advanced methods or implement my own solution which deviates from how the assignment requests it. –  Dec 24 '11 at 04:49
  • @fhaddad78 You can just wrap that code in a member function. std::string's member functions are still available. – Pubby Dec 24 '11 at 04:58
  • Thanks for the response. I overlooked calling operator[](x) directly and was trying to figure out how to use the infix notation. –  Dec 24 '11 at 05:15
  • 1
    @fhaddad78: `(*this)[x]`. `*this` yields `Pstring&`, on which you can call `operator[]`. – Xeo Dec 24 '11 at 06:20
1

The book didn't cover auto because that keyword was only recently added to the language. If your compiler is over a year old or not one of the big names it probably doesn't support it.

For this problem you don't need to access any member variables for a proper solution, so there's no need to worry about what they are or whether they're reachable. Good thing, because none of that is specified by the standard - it's all implementation details defined by your particular compiler, and if you find yourself digging that deeply you should ask yourself what you're doing wrong.

Of course member functions of the parent class are accessed exactly as member functions of the child class - you just call them.

Member operator overloads are a little trickier, but still not too bad. You need to supply the instance to invoke them against, which is *this. You can also call them with the operator keyword but in my opinion that's a little clumsier.

if ((*this)[i] == (*this)[j])

if (operator[](i) == operator[](j))
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 1
    `auto` the keyword has been around since B. `auto` doing something useful is what was recently added. – Pubby Dec 24 '11 at 05:11
  • @Mark Ransom OK. I'm going chalk this up to just a bad assignment all together. I was just really confused by this problem because it mentions using subscript notation in my solution which I don't understand how to do unless I were to overload the subscript operator or unless my string was sitting in a member variable which I could locate, but it seems like I can't. –  Dec 24 '11 at 05:12
  • 1
    @fhaddad78, I've updated the answer to include member operators. – Mark Ransom Dec 24 '11 at 05:45
  • @MarkRansom: Thank you for that. I was able to do it how I think the book is asking me to despite it being a little silly. I think the point of the whole thing was to get me to understand access specifiers, inheritance, etc. –  Dec 24 '11 at 06:08
0

If you do not want to use auto, then you can simply use std::string::iterator instead, which is what auto is resolving to anyways in this case.

Thus problem #1 is satisfied.


When you are calling begin() and end() you are calling the members begin() and end() in the superclass std::string.

Thus problem #2 is satisfied.

0

Try this:

#include <string>

class Pstring : public std::string
{
public:
    Pstring(const std::string &text)
        : std::string(text) { }

    bool isPalindrome()
    {
        std::string::size_type len = length();
        std::string::size_type half = len / 2;
        for (std::string::size_type idx = 0; idx < half; ++idx)
        {
            if ((*this)[idx] != (*this)[len-idx-1])
                return false;
        }
        return true;
    }
};
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770