Thread: derived class from string

  1. #16
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Previously posted by jlou
    Of course, the most important reason, in my opinion, is that string was not designed to be inherited from.

    Originally written by Bjarne
    Like other standard library types, a basic_string<T> is a concrete type without virtual functions. It can be used as a member when designing more sophisticated text manipulation classes, but it is not intended to be a base for derived classes.

    Originally posted by Codeplug
    Where does this say "don't inherit an object if it wasn't the programmers intention."?

    Originally posted by jlou
    First, who said that was the point of the quote?
    "Not intended to be used polymorphicly" is allot different than "Not designed to be inherited from".
    From The C++ Programming Language 3rd Edition by Bjarne Stroustrup
    [5] Don't derive from a concrete class
    This is just an OOP methology guideline. Inheritance is just one of several C++ language features that allow you to follow a particular OOP methology. If you want to attend the Church of Stroustrup, more power to you. I'm just not going to limit the use of a language feature simply because a particular OOP methology says [5].
    On a side note, I attend the church of design patterns, although I don't go every Sunday (as I'm sure you can tell). It follows similiar guidelines as those in Stoned_Coder's post.

    Originally posted by jlou
    In fact, I use a class derived from basic_string here at my work. ... So an easy workaround to prevent the undefined behaviour might even be of specific use to me. So please share it.
    I'm sure you'll be deleting that class via a basic_string pointer in your next project. And if you're truly worried about some morron doing that......then I feel your pain.

    If, for whatever reason, you do want to use polymorphism, then provide a proper base class for your users. Here's what I mean:
    Code:
    #include <iostream>
    #include <string>
    using namespace std;
    
    template<class _elem_t,
             class _traits_t = char_traits<_elem_t>,
             class _alloc_t = allocator<_elem_t> >
    class poly_basic_string : public basic_string<_elem_t, _traits_t, _alloc_t>
    {
    public:
        typedef _elem_t elem_t;
        typedef _traits_t traits_t;
        typedef _alloc_t alloc_t;
        typedef poly_basic_string<elem_t, traits_t, alloc_t> my_t;
        typedef basic_string<elem_t, traits_t, alloc_t> base_t;
    
        // basic constructors
        poly_basic_string(const elem_t *str) : base_t(str) {}
        poly_basic_string(const base_t &str) : base_t(str) {}
    
        // enforce virtual destruction
        virtual ~poly_basic_string() {}
    
        // assignment
        base_t& operator=(const base_t& str) {return base_t::operator=(str);}
    	base_t& operator=(const elem_t *str) {return base_t::operator=(str);}
        base_t& operator=(elem_t &e) {return base_t::operator=(e);}
    
        // allow children to hook into c_str() calls
        virtual const elem_t* c_str() const {return base_t::c_str();}
    };//poly_basic_string
    
    typedef poly_basic_string<char, char_traits<char>, allocator<char> > 
                poly_string;
    typedef poly_basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t> > 
                poly_wstring;
    
    class mystring : public poly_string
    {
        mutable int c_str_count;
    
    public:
        // basic constructors
        mystring(const elem_t *str) : poly_string(str), c_str_count(0) {}
        mystring(const base_t &str) : poly_string(str), c_str_count(0) {}
        
        virtual ~mystring() 
        {
            cout << "[mystring::c_str() called " << c_str_count << " times]" << endl;
        }//destructor
    
        // assignment
        base_t& operator=(const base_t& str) {return base_t::operator=(str);}
        base_t& operator=(const elem_t *str) {return base_t::operator=(str);}
        base_t& operator=(elem_t &e) {return base_t::operator=(e);}
    
        // count calls to c_str()
        virtual const elem_t* c_str() const
        {
            c_str_count++;
            return poly_string::c_str();
        }//c_str
    };//mystring
    
    void print(const poly_string *str)
    {
        cout << str->c_str() << endl;
    }//print
    
    int main()
    {
        poly_string *str = new mystring("Hello World");
    
        print(str);
    
        *str = "Hello there ";
        *str += "Enter some text: ";
        print(str);
    
        getline(cin, *str);
    
        cout << "You entered: ";
        print(str);
    
        delete str;
    
        return 0;
    }//main
    You could also use private or protected inheritance, or simply make the base class destructor virtual (making it non-concrete). Neither of these are helpful when extending basic_string and have their own set of pro's and con's.

    gg
    Last edited by Codeplug; 12-10-2003 at 02:46 AM.

  2. #17
    Registered User jlou's Avatar
    Join Date
    Jul 2003
    Posts
    1,090
    Previously posted by jlou
    Of course, the most important reason, in my opinion, is that string was not designed to be inherited from.

    Originally written by Bjarne
    Like other standard library types, a basic_string<T> is a concrete type without virtual functions. It can be used as a member when designing more sophisticated text manipulation classes, but it is not intended to be a base for derived classes.

    Originally posted by Codeplug
    Where does this say "don't inherit an object if it wasn't the programmers intention."?

    Originally posted by jlou
    First, who said that was the point of the quote?

    Originally posted by Codeplug
    "Not intended to be used polymorphicly" is allot different than "Not designed to be inherited from".
    I agree that the statement, "string was not designed to be inherited from," is different than, "Not intended to be used polymorphicly". But that seems to back up my point more than yours. My statement (and Stroustrup's) seems stronger than the polymorphism statement. So if a class was not intended to be a base for derived classes, then that includes being used polymorphicly, as well as any other use you might have for a derived class. Meaning, it was not intended for public derivation for any reason. Common sense then comes in and says, if it wasn't meant for this, then in general, don't do it. Where is the argument?

    ------

    Thank you for posting the code. Very nice. As it turns out, it won't help me personally, since it doesn't prevent the undefined behavior if a user deletes a basic_string pointer to an instance of my_string. Any code that could potentially do this (even though I am sure none does - we don't use string polymorphicly), uses basic_string because it has to deal with data from other libraries that have no knowledge of my_string. Adding poly_string wouldn't help because we wouldn't be able to add it those other libraries (if we could, we'd just add my_string).

    ------

    Since this thread is somewhat long, and I feel guilty for continuing to drag out this discussion without seemingly getting anywhere, I will try to summarize a little.
    [list=1][*]The STL string class was not intended to be a base for derived classes.

    a. I don't see how you can argue this unless you were on the standards committee and you intended for it to be a base class.

    b. This doesn't mean it should never be done, it is possible that there is a situation where it makes the most sense.

    c. If you do decide to use string as a base class, you should do so with the knowledge that it was not intended for that use, and there is the potential for undefined behavior if your class is used in a certain way. That certain usage is somewhat common for base and derived classes, but is much less common for classes derived from string.

    d. There are potentially better solutions beyond public derivation to extend the functionality of string.

    [*]It is generally considered a bad idea to publicly derive from a class without a virtual destructor, although some would argue that there are situations where it makes sense.

    a. One of the most important uses of public derivation is the ability to take advantage of virtual functions by using objects polymorphicly. If the base class has no virtual functions, then that advantage is not useful.

    b. It is possible for a user to attempt to delete the derived instance through a base class pointer, resulting in undefined behavior (and memory leaks, and everything else undefined behavior means).

    c. One reason that programmers will derive from a class with no virtual functions is to enhance the functionality of that type. Some believe that since they can guarantee the code leading to undefined behavior will never be written, deriving from a concrete type is safe, easier, and therefore better, than alternatives.

    d. There are others, myself included, who have never seen a situation where an alternative didn't exist that solved the problem and provided a better overall design.

    [*]The original point that led to the discussion was to warn the OP that deriving from string is not always a good idea, and that there might be alternative designs that are better.[/list=1]So please say something if you disagree with any of those points, or if you feel an additional points are important, or even if you agree with them. In fact, I welcome anybody else to chime in also. Since the OP hasn't posted a response in awhile, I don't feel so bad continuing to hijack this thread. Hopefully he will post here or start yet another thread if he still has questions.

  3. #18
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    I can agree with that, I'm just a little weary with 2's wording.
    It reads (to me) as if inheritance (as a C++ language feature), is only useful for implementing polymorphism.
    Or, in other words, I believe that 2 is only valid when the derivation is intended for polymorphic use.

    If you want to use inheritance to simply "bring in" the methods and members of another class, for whatever reason, then do so without restriction.

    Hardcore OOP methodologists may then argue: "what's to stop other users from using that derivation polymorphicly, which may cause potential problems since this wasn't intended by the base class?"
    Response: "the potential exists for users of STL to call vector<>::operator[] with OOB indicies. You can't always write 100% idiot proof API's."

    Don't get me wrong, I'm not anti-OOP. I believe more than anyone that inheritance and polymorphism are typically way overused, where simple encapsulation is the better design. I just get fired up when I see blanket statements like "Don't derive from a concrete class". Because what happens, is the student memorizes the statement only, and forgets why the statement was made.

    gg - master debater

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Inheritance Hierarchy for a Package class
    By twickre in forum C++ Programming
    Replies: 7
    Last Post: 12-08-2007, 04:13 PM
  2. Replies: 4
    Last Post: 03-03-2006, 02:11 AM
  3. Linked List Help
    By CJ7Mudrover in forum C Programming
    Replies: 9
    Last Post: 03-10-2004, 10:33 PM
  4. Headers that use each other
    By nickname_changed in forum C++ Programming
    Replies: 7
    Last Post: 10-03-2003, 04:25 AM
  5. creating class, and linking files
    By JCK in forum C++ Programming
    Replies: 12
    Last Post: 12-08-2002, 02:45 PM