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?
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.
new must not be given to a string*: deleting via such pointer will result in undefined behaviorAbout 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.
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With