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

M

Markus Dehmann

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
 
V

Victor Bazarov

Markus Dehmann said:
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
 
J

John Harrison

Markus Dehmann said:
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
 
J

Jeff Schwab

Markus said:
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 said:
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 said:
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( ) );
}
 

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
473,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top