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

Discussion in 'C++' started by Markus Dehmann, May 21, 2004.

  1. 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(value){}
    };

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

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

    int main(){
    vector <Value> val;
    values.push_back(IntValue(3));
    values.push_back(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::eek:stream & operator<<(std::eek:stream & out){}
    };

    So, how can I do it?

    Thanks
    Marks
     
    Markus Dehmann, May 21, 2004
    #1
    1. Advertising

  2. "Markus Dehmann" <> wrote...
    > 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!


    No, it wouldn't. You need at least a virtual destructor.

    > class IntValue : public Value{
    > private:
    > int _value;
    > public:
    > IntValue(int value):_value(value){}
    > };
    >
    > class StringValue : public Value{ // constructor etc omitted
    > private:
    > std::string _value;
    > public:
    > StringValue(std::string value):_value(value){}
    > };
    >
    > But how do I realize the << operator? I could do sth like this, then:
    >
    > int main(){
    > vector <Value> val;
    > values.push_back(IntValue(3));


    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.

    > values.push_back(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::eek:stream & operator<<(std::eek:stream & out){}
    > };
    >
    > So, how can I do it?


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

    V
     
    Victor Bazarov, May 21, 2004
    #2
    1. Advertising

  3. "Markus Dehmann" <> wrote in message
    news:...
    > 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(value){}
    > };
    >
    > class StringValue : public Value{ // constructor etc omitted
    > private:
    > std::string _value;
    > public:
    > StringValue(std::string value):_value(value){}
    > };
    >
    > But how do I realize the << operator? I could do sth like this, then:
    >
    > int main(){
    > vector <Value> val;
    > values.push_back(IntValue(3));
    > values.push_back(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::eek:stream & operator<<(std::eek:stream & out){}
    > };
    >
    > So, how can I do it?


    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(value){}

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

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

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

    std::eek:stream & operator<<(std::eek:stream & 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
    www.boost.org

    john
     
    John Harrison, May 21, 2004
    #3
  4. Markus Dehmann

    Jeff Schwab Guest

    Markus Dehmann wrote:
    > 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.


    OK, templates and the whole STL is designed to support this sort of thing.

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


    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?

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


    #include <string>

    > class StringValue : public Value{ // constructor etc omitted
    > private:
    > std::string _value;
    > public:
    > StringValue(std::string value):_value(value){}
    > };
    >
    > But how do I realize the << operator? I could do sth like this, then:


    #include <iostream>
    #include <vector>

    > int main(){


    using namespace std;

    > vector <Value> val;
    > values.push_back(IntValue(3));


    Is "values" supposed to be the same object as "val"?

    > values.push_back(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::eek:stream & operator<<(std::eek:stream & out){}
    > };


    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. ;)

    > So, how can I do it?


    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::eek:stream& ) const =0;
    };

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

    std::eek:stream& operator << ( std::eek:stream& 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_back( new Value< int >( 3 ) );
    values.push_back( new Value< std::string >( "test" ) );

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

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

    std::for_each( values.begin( ), values.end( ), Deleter( ) );
    }
     
    Jeff Schwab, May 21, 2004
    #4
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Blmn
    Replies:
    0
    Views:
    456
  2. Alf P. Steinbach
    Replies:
    6
    Views:
    560
    John Carson
    Sep 3, 2005
  3. =?utf-8?B?Qm9yaXMgRHXFoWVr?=

    Base class for file-like objects? (a.k.a "Stream" in Java)

    =?utf-8?B?Qm9yaXMgRHXFoWVr?=, Jul 24, 2007, in forum: Python
    Replies:
    4
    Views:
    324
    George Sakkis
    Jul 25, 2007
  4. Hicham Mouline
    Replies:
    1
    Views:
    604
    Victor Bazarov
    Apr 20, 2009
  5. Karan Rajput
    Replies:
    2
    Views:
    150
    Abinoam Jr.
    Dec 22, 2010
Loading...

Share This Page