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.