3

I'm learning C++ and want to implement a custom string class, MyTextProc::Word, to add some features to std::string, such as string reversal, case conversion, translations etc.

It seems that this is best done using an is-a relationship:

namespace MyTextProc {
   class Word : public string {
      /* my custom methods.... */
   };
}

I do not specify any constructors for my class but the above definition of the Word class only exposes default void and copy constructors - cant Word just inherit all the public string constructors as well?

It would be good to have Word work just like a string. I am adding no properties to string; must I really implement every single constructor of string, the base class, in order to implement my new subclass?

Is this best done using a has-a relationship? Should I just implement a const string& constructor and require clients to pass a string object for construction? Should I override all of the string constructors?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
CJC
  • 43
  • 3
  • 1
    Please see this previous post concerning why http://stackoverflow.com/questions/6006860/why-should-one-not-derive-from-c-std-string-class Why should one not derive from c++ std string class? – Shafik Yaghmour Feb 24 '13 at 18:13
  • On a side note, a string can contain white space including line separators. Are you sure this is what you want? – Carl Feb 24 '13 at 20:34
  • I was actually going to use the class to only store words with no white space. The words would be created from parsing text. – CJC Feb 24 '13 at 21:56

2 Answers2

3

Welcomne to the C++ hell.

You've just touched one of the most controversial aspect of C++: std::string is not polymorphic and constructors are not inherited.

The only "clean" way (that will not make any sort of criticism) is embed std::string as a member, delegating ALL OF ITS METHODS. Good work!

Other ways can come around, but you have always to take care some limitations.

  • std::string has no virtual methods, so if you derive it, you will not get a polymorphic type.
  • That means that if yoy pass a Word to a sting keeping function, and that function calls a string method, your override method will not be called and
  • whatever allocation of Word via new must not be given to a string*: deleting via such pointer will result in undefined behavior
  • All the inherited method that take string and return string-s will work, but they'll return string, not Word.

About constructors, they are NOT INHERITED. The default construction inheritance is an illusion, due to the compiler synthesized default implementation of default, copy and assign, that call implicitly the base.

In C++11 a workaround can be

class Word: public std::string
{
public:
    template<class... Args>
    Word(Args&&... args) :std::string(std::forward<Args>(args)...) 
    {}

    //whatever else   
};

This makes whatever kind of arguments to be given in a call to a suitable std::sting ctor (if it exist, otherwise a compile error happens).

Now, decide yourself what the design should be. May be you will come with normal std::string and an independent set of free functions.

Another (imperfect) way can be make Word not inheriting, but embedding std::string, constructed as above, and implicitly convertible into std::string. (plus having a str() explicit method). This let you able to use a Word as a string, create a Word from a string, but not use a Word "in place of" a string.

Another thing to unlearn (may be from Java ...): don't fix yourself to "is-a = inheritance and has-a = embedding" OOP rule. All C++ standard library objects are not Objects in OOP sense, so all the OOP school methodologies have fallacies in that context.

You have to decide in you case what is the trade-off between simple coding (and good application of the "don't repeat yourself" paradigm, much easier with imheritance) or simple maintenance (and embedding let your code less prone to be used wrongly by others)


This is in answer t othe comment below:

"the lack of polymorphism of standard C++ classes. Why is this so? It seems to a novice like me that not implementing std C++ libs using virtual functions is defeating the point of the language they are designed to enrich!!!"

Well ... yes and not!

Since you cite PERL, consider that - PERL is a scripting language, where types are dynamic. - Java is a language where types are static and objects dynamic - C++ is a language where types are static and object are static (and dynamic allocation of object is explicit)

Now, in Java objects are always dynamically allocated and local variables are "reference" to those objects. In C++, local variables are object themselves, and have value semantics. And the C++ standard library is designed not as a set of bases to extend, but as a set of value types for which generate code by means of templates.

Think to an std::string as something working just like int works: do you expect to derive from int to get "more methods" or to change the behavior of some of them?

The controversial aspect, here, is that -to be coherent with this design- std::string should just manage its internal memory, but should have no methods. Istead, string functions shold have been implemented as templates, so that they can be used as "algorithm" with whatever other class exhibiting a same std::string external behavior. Something the designers didn't.

They placed many methods on it, but they din't make it polymorphic to still retain the value semantics, thus making and ambiguous design, and retaining to inheritance the only way to "reuse" those methods without re-declaring them. This is possible, but with the limitation I told you.

If yo uwant to effectively create new function, to have "polymorphism on value", use teplates: intead of

std::string capitalize(const std::string& s) { .... }

do something like

template<class String>
String capitalize(const String& s) { .... }

So that you code can work with whatever class having the same string interface respect to characters, for whatever type of characters.

Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
  • thanks for a superb post... I was just looking for some simple coding problems to help me learn the basics I didnt mean to delve into these depths just yet! Im thinking maybe I should just stick to PERL! – CJC Feb 24 '13 at 19:00
  • I have digested your post. An i am thinking i want my Word to behave like a string but just add some functions so the free functions are probably correct in this case. RE the lack of polymorphism of standard C++ classes. Why is this so? It seems to a novice like me that not implementing std C++ libs using virtual functions is defeating the point of the language they are designed to enrich!!! – CJC Feb 24 '13 at 19:11
  • Again many thanks for your advise. I can follow your direction but dont quite understand your argument though - i thought C++ represented everything as objects so int works with iostream and such. Anyway I take your word for it, hopefully all will become clear in time! Respect – CJC Feb 24 '13 at 22:02
  • @CJC:Don't worry, It took me *ten years* (see http://norvig.com/21-days.html ) to get it ;-)) ! – Emilio Garavaglia Feb 25 '13 at 07:47
2

As honest advise, I'd implement the methods you want as functions which take in a string and return a string. They'll be easier to test, decoupled, and easy to use. When in C++, don't always reach for a class when a function would do. In fact, when you get into templates, you could create a templated function without definition and a specialization for the basic string class. That way, you always will know if the string type you're touching has a custom defined method (and yes, if you interact with Microsoft you'll discover there's 50 million string implementations.)

wheaties
  • 35,646
  • 15
  • 94
  • 131
  • Thanks Guys. It's going to take some time to absorb some of the details of that post Shafik! So what about using a just using Word to encapsulate a string object for storage and implement the required functions? Is this any better than just implementing a library of string manipulation functions? – CJC Feb 24 '13 at 18:25
  • 2
    Unless you're trying to hide that information is held and manipulated as a string, you're best to create functions. Classes and hence encapsulation are only valid for information hiding. Are you truly trying to hide implementation details? It doesn't sound like it. – wheaties Feb 24 '13 at 18:35
  • Ok i might be clutching at objects here but but I assume if I also wanted to associate data with the strings such as font or colour then encapsulation would be valid - or is your response to this "use structs"! – CJC Feb 24 '13 at 18:49
  • Now that's a different use case. If you're trying to associate additional state, by all means use a class. – wheaties Feb 24 '13 at 18:54