Empty base class (like Java's "interface")?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Markus Dehmann

    Empty base class (like Java's "interface")?

    I have a two different value types with which I want to do similar
    things: store them in the same vector, stack, etc. Also, I want an <<
    operator for each of them.

    class Value{}; // this would be "public interface Value{}" in Java!

    class IntValue : public Value{
    private:
    int _value;
    public:
    IntValue(int value):_value(v alue){}
    };

    class StringValue : public Value{ // constructor etc omitted
    private:
    std::string _value;
    public:
    StringValue(std ::string value):_value(v alue){}
    };

    But how do I realize the << operator? I could do sth like this, then:

    int main(){
    vector <Value> val;
    values.push_bac k(IntValue(3));
    values.push_bac k(StringValue(" test"));
    for ( vector<Value>:: iterator it = val.begin(); it != val.end();
    ++it ){
    cout << *it;
    }
    }

    I tried defining it empty in the base class and with a non-empty
    implementation in IntValue and StringValue, but it gives me compile
    errors (can't find the operator)

    class Value{
    std::ostream & operator<<(std: :ostream & out){}
    };

    So, how can I do it?

    Thanks
    Marks
  • Victor Bazarov

    #2
    Re: Empty base class (like Java's &quot;interface &quot;)?

    "Markus Dehmann" <markus.cl@gmx. de> wrote...[color=blue]
    > I have a two different value types with which I want to do similar
    > things: store them in the same vector, stack, etc. Also, I want an <<
    > operator for each of them.
    >
    > class Value{}; // this would be "public interface Value{}" in Java![/color]

    No, it wouldn't. You need at least a virtual destructor.
    [color=blue]
    > class IntValue : public Value{
    > private:
    > int _value;
    > public:
    > IntValue(int value):_value(v alue){}
    > };
    >
    > class StringValue : public Value{ // constructor etc omitted
    > private:
    > std::string _value;
    > public:
    > StringValue(std ::string value):_value(v alue){}
    > };
    >
    > But how do I realize the << operator? I could do sth like this, then:
    >
    > int main(){
    > vector <Value> val;
    > values.push_bac k(IntValue(3));[/color]

    This is nonsense. Read about 'slicing' and 'heterogenous containers'.
    In short, if you attempt to stor 'IntValue' in a vector<Value>, you
    will store only the base part of the 'IntValue' and lose all of its
    identity. Store pointers.
    [color=blue]
    > values.push_bac k(StringValue(" test"));
    > for ( vector<Value>:: iterator it = val.begin(); it != val.end();
    > ++it ){
    > cout << *it;
    > }
    > }
    >
    > I tried defining it empty in the base class and with a non-empty
    > implementation in IntValue and StringValue, but it gives me compile
    > errors (can't find the operator)
    >
    > class Value{
    > std::ostream & operator<<(std: :ostream & out){}
    > };
    >
    > So, how can I do it?[/color]

    Google the newsgroup archives. Has been done before, has been written
    about dozens of times. No need to repeat.

    V


    Comment

    • John Harrison

      #3
      Re: Empty base class (like Java's &quot;interface &quot;)?


      "Markus Dehmann" <markus.cl@gmx. de> wrote in message
      news:c1e48b51.0 405202058.36d10 b76@posting.goo gle.com...[color=blue]
      > I have a two different value types with which I want to do similar
      > things: store them in the same vector, stack, etc. Also, I want an <<
      > operator for each of them.
      >
      > class Value{}; // this would be "public interface Value{}" in Java!
      >
      > class IntValue : public Value{
      > private:
      > int _value;
      > public:
      > IntValue(int value):_value(v alue){}
      > };
      >
      > class StringValue : public Value{ // constructor etc omitted
      > private:
      > std::string _value;
      > public:
      > StringValue(std ::string value):_value(v alue){}
      > };
      >
      > But how do I realize the << operator? I could do sth like this, then:
      >
      > int main(){
      > vector <Value> val;
      > values.push_bac k(IntValue(3));
      > values.push_bac k(StringValue(" test"));
      > for ( vector<Value>:: iterator it = val.begin(); it != val.end();
      > ++it ){
      > cout << *it;
      > }
      > }
      >
      > I tried defining it empty in the base class and with a non-empty
      > implementation in IntValue and StringValue, but it gives me compile
      > errors (can't find the operator)
      >
      > class Value{
      > std::ostream & operator<<(std: :ostream & out){}
      > };
      >
      > So, how can I do it?[/color]

      Buy a book on C++, look up the chapter on polymorphism and virtual
      functions. Also don't try to program C++ by analogy with Java, you'll end up
      programming only the common ground between Java and C++ which won't get you
      very far. Here's some untested sample code

      class Value
      {
      public:
      virtual ~Value() {}
      virtual void print(ostream& os) const = 0;
      };

      class IntValue : public Value{
      private:
      int _value;
      public:
      IntValue(int value):_value(v alue){}

      void print(ostream& os) const
      {
      os << _value;
      }
      };

      class StringValue : public Value{ // constructor etc omitted
      private:
      std::string _value;
      public:
      StringValue(std ::string value):_value(v alue){}

      void print(ostream& os) const
      {
      os << _value;
      }
      };

      std::ostream & operator<<(std: :ostream & out, const Value& x)
      {
      x.print(out);
      return out;
      }

      And of course like Victor says, you must store pointers or smart pointers in
      your vector. Try googling for smart pointer, or check out shared_ptr at


      john


      Comment

      • Jeff Schwab

        #4
        Re: Empty base class (like Java's &quot;interface &quot;)?

        Markus Dehmann wrote:[color=blue]
        > I have a two different value types with which I want to do similar
        > things: store them in the same vector, stack, etc. Also, I want an <<
        > operator for each of them.[/color]

        OK, templates and the whole STL is designed to support this sort of thing.
        [color=blue]
        > class Value{}; // this would be "public interface Value{}" in Java![/color]

        As Victor said, not really. In C++, you rarely need empty interfaces.
        If your reason for creating a common base class is to express a set of
        operations common to different sub-classes, why is the base empty?
        [color=blue]
        > class IntValue : public Value{
        > private:
        > int _value;
        > public:
        > IntValue(int value):_value(v alue){}
        > };[/color]

        #include <string>
        [color=blue]
        > class StringValue : public Value{ // constructor etc omitted
        > private:
        > std::string _value;
        > public:
        > StringValue(std ::string value):_value(v alue){}
        > };
        >
        > But how do I realize the << operator? I could do sth like this, then:[/color]

        #include <iostream>
        #include <vector>
        [color=blue]
        > int main(){[/color]

        using namespace std;
        [color=blue]
        > vector <Value> val;
        > values.push_bac k(IntValue(3));[/color]

        Is "values" supposed to be the same object as "val"?
        [color=blue]
        > values.push_bac k(StringValue(" test"));
        > for ( vector<Value>:: iterator it = val.begin(); it != val.end();
        > ++it ){
        > cout << *it;
        > }
        > }
        >
        > I tried defining it empty in the base class and with a non-empty
        > implementation in IntValue and StringValue, but it gives me compile
        > errors (can't find the operator)
        >
        > class Value{
        > std::ostream & operator<<(std: :ostream & out){}
        > };[/color]

        You're close. This would define a way to insert the stream into the
        Value, rather than the other way around. Probably not what you want. ;)
        [color=blue]
        > So, how can I do it?[/color]

        Define the operator as a free-standing function, and have it call a
        print method declared in the base class.


        You might want to adjust your viewpoint a bit when programming in C++.
        When you consider a data type or operation, keep it as generic as
        possible. Instead of defining two wrapper classes that are identical
        except for the type of value they wrap, define a class template for such
        wrappers. Instead of looping over the contents of a container (which
        you are likely to do pretty frequently), use the existing library
        facilities, and only write new code to represent the new or original
        part of your program (not the looping code). You'll end up with lots of
        short blocks and classes; it will feel inefficient, because you'll be
        producing a lot of "overhead" code (class declarations and the like) for
        each simple operation. However, the savings you will get in the long
        term, not only in development time but in maintenance, might just
        impress you so much with C++ that you find yourself using it for almost
        everything.

        Here's an example of one way to write something like the code above.
        I've used explicit dynamic allocation, to match what I think you're
        trying to do. Notice that the memory has to be deleted manually.

        One other thing worth mentioning is that I already have built a library
        of utility classes like Dereferencer and Deleter below, so I don't have
        to redefine them every time I use them. If you're new to C++, but
        already know a different language, this is probably a good time to start
        building your own library of utilities.

        #include <iosfwd>

        struct Value_base
        {
        virtual ~Value_base ( ) { }
        virtual void print ( std::ostream& ) const =0;
        };

        template< typename T >
        struct Value: public Value_base
        {
        Value ( T const& t = T( ) ): m_t( t ) { }
        void print ( std::ostream& out ) const { out << m_t; }
        private:
        T m_t;
        };

        std::ostream& operator << ( std::ostream& out, Value_base const& v )
        {
        v.print( out );
        return out;
        }

        struct Dereferencer
        {
        template< typename T >
        T const& operator ( ) ( T const* p ) const { return *p; }
        };

        struct Deleter
        {
        template< typename T >
        void operator ( ) ( T const* p ) const { delete p; }
        };

        #include <algorithm>
        #include <iostream>
        #include <iterator>
        #include <ostream>
        #include <string>
        #include <vector>

        int main ( )
        {
        typedef std::vector< Value_base* > Vector;

        Vector values;

        values.push_bac k( new Value< int >( 3 ) );
        values.push_bac k( new Value< std::string >( "test" ) );

        std::ostream_it erator< Value_base > out( std::cout, "\n" );
        Dereferencer deref;

        std::transform( values.begin( ), values.end( ), out, deref );

        std::for_each( values.begin( ), values.end( ), Deleter( ) );
        }

        Comment

        Working...