STL string inheritance

M

Mike Tyka

Hello community,

i'm fairly new to using the STL but i've been experimenting a bit with it.
I tried to derive a new class say MyString from string like so:

class MyString: public string{
public:
MyString(){}
};

when i then try to do:

MyString MyStringInstance;
MyStringInstance = "BlaBlaBla";

it moans:
error C2679: binary '=' : no operator found which takes a right-hand operand
of type 'const char [9]' (or there is no acceptable conversion)

MyStringInstance += "BlaBlaBla";

and

string MyStringInstance;
MyStringInstance = "BlaBlaBla";

works fine though.

Why is the = operator not available in MyString ??

Thanks for any help, Mike :)

P.S. I'm using MS Visual C++ .NET
 
F

Fabio

Mike Tyka said:
Hello community,

i'm fairly new to using the STL but i've been experimenting a bit with it.
I tried to derive a new class say MyString from string like so:

class MyString: public string{
public:
MyString(){}
};

when i then try to do:

MyString MyStringInstance;
MyStringInstance = "BlaBlaBla";

it moans:
error C2679: binary '=' : no operator found which takes a right-hand operand
of type 'const char [9]' (or there is no acceptable conversion)

MyStringInstance += "BlaBlaBla";

and

string MyStringInstance;
MyStringInstance = "BlaBlaBla";

works fine though.

Why is the = operator not available in MyString ??

Thanks for any help, Mike :)

P.S. I'm using MS Visual C++ .NET

operator=() is not directly inherited by the derived class, it is the
standard C++ behaviour (IMHO)

Fabio
 
P

Peter Koch Larsen

Fabio said:
Hello community,

i'm fairly new to using the STL but i've been experimenting a bit with it.
I tried to derive a new class say MyString from string like so:

class MyString: public string{
public:
MyString(){}
};

when i then try to do:

MyString MyStringInstance;
MyStringInstance = "BlaBlaBla";

it moans:
error C2679: binary '=' : no operator found which takes a right-hand operand
of type 'const char [9]' (or there is no acceptable conversion)

MyStringInstance += "BlaBlaBla";

and

string MyStringInstance;
MyStringInstance = "BlaBlaBla";

works fine though.

Why is the = operator not available in MyString ??

Thanks for any help, Mike :)

P.S. I'm using MS Visual C++ .NET

operator=() is not directly inherited by the derived class, it is the
standard C++ behaviour (IMHO)

Fabio

Rubbish! Of course it is.


/Peter
 
R

Rob Williscroft

Peter Koch Larsen wrote in @news000.worldonline.dk in comp.lang.c++:
Rubbish! Of course it is.

Not so, Fabio is correct (*), operator = if not declared by the user
is generated by the compiler (uses member/base-wise assignment),
hence the compiler generated operator = hides the base class
operator =.

*) This depends on the interpretation of "directly inherited"
of course :).

The easiest way to declare operator = is:

#include <iostream>
#include <string>

class MyString: public std::string
{
public:

MyString(){}

using std::string::eek:perator =;
};

Alas this won't work for constructors.

Rob.
 
D

David Hilsee

Mike Tyka said:
Hello community,

i'm fairly new to using the STL but i've been experimenting a bit with it.
I tried to derive a new class say MyString from string like so:

class MyString: public string{
public:
MyString(){}
};
<snip>

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.
 
K

Karthik Kumar

David said:
<snip>

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;
};
 
P

Peter Koch Larsen

Rob Williscroft said:
Peter Koch Larsen wrote in @news000.worldonline.dk in comp.lang.c++:


Not so, Fabio is correct (*), operator = if not declared by the user
is generated by the compiler (uses member/base-wise assignment),
hence the compiler generated operator = hides the base class
operator =.

*) This depends on the interpretation of "directly inherited"
of course :).

I believe we are in agreement here. To sum it up: operator= behaves exactly
like any other member function or operator, but since the compiler generates
its own operator= you can not use it without some trickery.

/Peter

[snip]
 
G

Gavin Deane

Karthik Kumar said:
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.

The only thing you absolutely MUST not do is delete the derived
objects polymorphically because the base class doesn't have a virtual
destructor. But as long as you understand that, then there's no
problem. As a practical matter, you won't be able to do anything
polymorphically because the std containers don't have any virtual
functions. But in this case polymorphism isn't the goal - a new class
with the container's functionality plus more of your own is.

[*] I'm assuming that if you're considering inheritance, you're
thinking OO.

GJD
 
D

David Hilsee

Gavin Deane said:
Karthik Kumar <[email protected]> wrote in message
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. 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. 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.
The only thing you absolutely MUST not do is delete the derived
objects polymorphically because the base class doesn't have a virtual
destructor. But as long as you understand that, then there's no
problem. As a practical matter, you won't be able to do anything
polymorphically because the std containers don't have any virtual
functions. But in this case polymorphism isn't the goal - a new class
with the container's functionality plus more of your own is.

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.

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.
 
M

Mike Tyka

David Hilsee said:
Gavin Deane said:
Karthik Kumar <[email protected]> wrote in message
with
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. 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. 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.
The only thing you absolutely MUST not do is delete the derived
objects polymorphically because the base class doesn't have a virtual
destructor. But as long as you understand that, then there's no
problem. As a practical matter, you won't be able to do anything
polymorphically because the std containers don't have any virtual
functions. But in this case polymorphism isn't the goal - a new class
with the container's functionality plus more of your own is.

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.

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.

Thanks a lot for all your answers, i think i'm slowly getting my head round
this stuff.. I kept the example generic for simplicity's sake.
I want to create a data structure and a correcponding mask data structure,
both of
which have all of the properties of a string, and it would be useful to have
all it's functionality
(like cutting, concatenating etc etc). At the same time both data structures
will want to have
additional functionality, specialised to the type of data they're holding,
but no more additional
data - so the lack of virtual destructors etc shouldnt matter !?Hence the
inherited approach seems
sensible in this case ?

Am i thinking along the right lines here ?

Mike :)
 
D

David Hilsee

Mike Tyka said:
Thanks a lot for all your answers, i think i'm slowly getting my head round
this stuff.. I kept the example generic for simplicity's sake.
I want to create a data structure and a correcponding mask data structure,
both of
which have all of the properties of a string, and it would be useful to have
all it's functionality
(like cutting, concatenating etc etc). At the same time both data structures
will want to have
additional functionality, specialised to the type of data they're holding,
but no more additional
data - so the lack of virtual destructors etc shouldnt matter !?Hence the
inherited approach seems
sensible in this case ?

Am i thinking along the right lines here ?

Even if the derived class contains no additional data, it's still undefined
behavior if you pass delete a std::string pointer when it is a pointer to an
instance of the derived class. Of course, you can avoid doing that in your
programs, and it's hard to accidentally do it simply because std::string has
no virtual member functions.

I tend to gripe about public inheritance of standard containers because it
is using inheritance to expose an implementation detail as a "nameless
public member". Many people choose to do that instead of having a public
member because inheritance is generally considered "better" than public
members. Unfortunately, in this case, it's roughly the same thing. It's a
general problem when inheritance is used for code reuse purposes (discussed
in the FAQ a bit in the Smalltalk/C++ comparison -- 30.4). However, for
most smaller programs, it doesn't really matter.
 
G

Gavin Deane

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.
 
D

David Hilsee

Gavin Deane said:
"David Hilsee" <[email protected]> wrote in message

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.

I don't think the distinct type is necessary. The std::vector<char> base
would be publicly exposed for anyone to modify in any way they please. Its
contents could easily (and accidentally) be replaced with an arbitrary
sequence of characters that isn't one of your messages, and there's nothing
the derived class can do about that. The distinct type doesn't offer any
significant benefit that I can see. I'm not really sure what you mean by
"doesn't make sense". Maybe you meant "I'm never going to do that"? If
there is an invalid sequence of characters that "doesn't make sense" for
your messages, then I would revisit the private member solution.

I've never tried this idea, but to me personally it definitely doesn't
feel right, whereas inheritance does.

Many people would say that, and I think that might have something to do with
all of the "OO hype" that people hear from time to time. I'd argue that
they're about the same.
 
G

Gavin Deane

David Hilsee said:
I don't think the distinct type is necessary.

It makes the code more closely model the problem, making the intent
clearer.

some_return_type parse_message(const std::vector<char>& msg, ...) {
.... }

doesn't make sense where

some_return_type parse_message(const my_message_type& msg, ...) { ...
}

does because the function isn't for parsing a vector<char>, it's for
parsing a my_message_type. Again, my ideal solution would be a strong
typedef but we don't have those.
The std::vector<char> base
would be publicly exposed for anyone to modify in any way they please. Its
contents could easily (and accidentally) be replaced with an arbitrary
sequence of characters that isn't one of your messages, and there's nothing
the derived class can do about that.

The same risk exists with a public member or using a vector directly.
So here inheritance doesn't add anything, but it doesn't lose anything
either.
The distinct type doesn't offer any
significant benefit that I can see. I'm not really sure what you mean by
"doesn't make sense". Maybe you meant "I'm never going to do that"? If
there is an invalid sequence of characters that "doesn't make sense" for
your messages, then I would revisit the private member solution.

Whether the sequence of characters in the message object makes sense
has nothing to do with why I want a distinct type. There may (now or
in the future) be other vector<char> objects around that are used for
completely different things. It seems preferable to me to be able to
write a parse function that can only accept my messages (which may or
may not turn out to be full of garbage). Without a distinct type, I'd
be able to call the parse function for a vector<char> that wasn't a
message, which would make no more sense that calling it for an int.

<snip>
 
D

Daniel T.

Mike Tyka said:
Thanks a lot for all your answers, i think i'm slowly getting my head round
this stuff.. I kept the example generic for simplicity's sake.
I want to create a data structure and a correcponding mask data structure,
both of
which have all of the properties of a string, and it would be useful to have
all it's functionality
(like cutting, concatenating etc etc). At the same time both data structures
will want to have
additional functionality, specialised to the type of data they're holding,
but no more additional
data - so the lack of virtual destructors etc shouldnt matter !?Hence the
inherited approach seems
sensible in this case ?

Is it the case that any sequence of characters that can be held in a
std::string, can also be held (and make sense) in one of your
specialized types?

If yes, then your specialized type isn't adding anything new, just use a
std::string. If no, then publicly inheriting from std::string is
dangerous because it exposes your type to misuse.

Either use private inheritance, or containment in the latter case.

Am i thinking along the right lines here ?

Ask yourself, "what are the invariants that exist for my specialized
type that are different than those that exist for std::string?" Then you
will be thinking along the right lines.
 
D

David Hilsee

Whether the sequence of characters in the message object makes sense
has nothing to do with why I want a distinct type. There may (now or
in the future) be other vector<char> objects around that are used for
completely different things. It seems preferable to me to be able to
write a parse function that can only accept my messages (which may or
may not turn out to be full of garbage). Without a distinct type, I'd
be able to call the parse function for a vector<char> that wasn't a
message, which would make no more sense that calling it for an int.

My point is that even with the new type, after a simple assignment, your
functions can be called for a std::vector<char> that wasn't a message. It
offers about as much protection as the typedef, so I didn't see any benefit
of a separate class over a typedef. It might guard against cases where the
programmer _accidentally_ passed your function the wrong std::vector<char>,
but I can't imagine that happening much, in practice. If your functions
work for any std::vector<char> (as they should, given the public
inheritance), then I'd just use a typedef to express the intent that it
should only be used for messages.
 
M

Markus Elfring

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.

These templates are not designed so that they can be used as base
classes for polymorphism.
Their intended use is for value objects.
http://c2.com/cgi/wiki?ValueObject
 
M

Markus Elfring

[...]
The only thing you absolutely MUST not do is delete the derived
objects polymorphically because the base class doesn't have a virtual
destructor. But as long as you understand that, then there's no
problem. As a practical matter, you won't be able to do anything
polymorphically because the std containers don't have any virtual
functions. [...]

Should the standard collections be treated as final classes like it
can be specified with a key word in Java?
 
G

Gavin Deane

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.

These templates are not designed so that they can be used as base
classes for polymorphism.
Their intended use is for value objects.
http://c2.com/cgi/wiki?ValueObject

Where do I mention polymorphism in the passage you quoted? If you read
the rest of the discussion you'll see that I wasn't talking about
using inheritance to get polymorhic behaviour at all.

In fact, in part my post that you snipped, I specifically said that
you wouldn't be able to use a classes derived from containers
polymorhically because containers have no virtual functions.
 
R

Ron Natalie

Fabio said:
operator=() is not directly inherited by the derived class, it is the
standard C++ behaviour (IMHO)
Standard behavior. The reason is simple. If you don't declare an operator=,
the compiler implicitly generates one for you. That hides any base class operator=.
Of course, if you do declare an operator=, it hides a base class operator=
so the effect is the same.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,176
Messages
2,570,950
Members
47,503
Latest member
supremedee

Latest Threads

Top