David Hilsee said:
Gavin Deane said:
Karthik Kumar <
[email protected]> wrote in message news:
[email protected]...
I always feel the urge to say this, so I'm saying it. First tip about the
standard container classes: they were not designed for inheritance. The
container classes have no virtual member functions (even the important
destructor) and no standard protected members. If MyString is just going to
hold a bunch of utility functions for std::string, then just use non-member
functions (possibly in a namespace) instead.
Or you could think of 'aggregation' relationships a.k.a member
relationships.
class MyString {
public:
MyString(const string & ref) : str(ref);
// Methods that operate on the member variable 'str'.
// ......................
private:
string str;
};
Then you have to explicitly forward any parts of std::string's
interface that you want MyString to have. If you want a restricted
interface (like the standard sequence adapters) this might be the
right solution.
But if you want all the functionality of a std container plus some
more of your own, and your OO design [*] calls for a new type to
provide this functionality, then inheriting from the container might
be the simplest solution.
I have not yet seen any "OO design" that irrefutably called for inheritance
from a standard container.
Very few design decisions are irrefutable. There are almost always
alternatives.
There is some blurring going on here between strings, where the OP
started, and the standard containers, which came up during the
discussion, and are much more general purpose than std::string.
I have never needed to create my own string type. Indeed I have
suffered in situations where too many different string types coexist.
But I have on a number of occasions wanted to add functionality to a
continer for a specific purpose.
If utility functions for std::string are
desired, they do not need to be written in a derived class. In fact, it is
better if they are not, because they are more easily reused in programs that
use std::string and not MyString. Most of the time, this is what is
desired.
Again, thinking containers rather than strings ...
I absolutely agree with that, but I'm not talking about utility
functions that could meaningfully apply to any container of the right
type.
As an example, I wrote a piece of software that communicated with some
electronics by reading and writing sequences of bytes over a serial
port. I wanted the messages in a std::vector<char> because I needed to
do vector-related things like iterating, push_backing etc. But I also
needed functionality very specific to my messages - parsing, building
up parts of the message from field specifier objects I had, for
example.
I could have done it all with free functions that took a vector<char>
and operated on it via its public interface only. But that seemed
wrong because it would make absolutley no sense to try and parse
something that happens to be a vector<char> but isn't one of my
messages.
What I really wanted was something like
typedef std::vector<char> my_message_type;
where typedef really does define a new and distinct type. But the
language doesn't provide that.
If the intention is to provide a string class that could later
have its std::string representation replaced, then the std::string should
probably be a private member.
Agreed. But that wasn't my intention either.
I'm not sure if that's best described as a goal or as a solution to a
problem, and a solution that usually has better alternatives at that.
Well in the project I described above, the messages were the whole
point of the work. A type that could represent the messages was the
fundamental design goal.
In other similar situations I've tried alternatives and never come
across one that's as clean as inheritance.
If you honestly need a class that publicly exposes std::string's interface
and has more functionality thrown in, then I think the following is a little
more "honest":
class MyString {
public:
// constructors, etc
std::string s;
// additional stuff here
};
To access the string, simply write foo.s.blah() instead of foo.blah(). This
avoids forwarding functions at the cost of two extra keystrokes. It also
avoids the "delete pString" issue. Most people would say that it doesn't
feel right, but it's about the same as public inheritance from the
std::string.
I've never tried this idea, but to me personally it definitely doesn't
feel right, whereas inheritance does.