views:

453

answers:

4

I'm coming from the Java world and are building a small c++ program at the moment. I have an object that does some work and then returns the result of the work as a list.

Now a day later i changed the behavior of the object to save the results in a set to avoid duplicates in the container. But I can't simply return the set because I used a list for the interface in the first time. Is there a common container interface that I can use to specify the interface of my object and forget about the container type I use internally?

At the moment I'm creating a set adding all the values and then creating a list from the set:

return std::list<foo>(this->mySet.begin(), this->mySet.end())

Seems a little strange.

+9  A: 

The whole C++- standard library including its containers is - unlike Java - not interface (inheritance, polymorphism)- but template-based (for the sake of efficiency).

You could create a polymorphic wrapper around your collection but this isn't the C++-way.

The simplest solution is just to simplify the programm with some type aliases:

#include <iostream>
#include <list>
#include <vector>

using namespace std;

class Test {

private:
    typedef vector<int> Collection;

    Collection c;

public:

    typedef Collection::const_iterator It;

    void insert(int Item) {
     c.push_back(Item);
    }

    It begin() const { return c.begin(); }
    It end()   const { return c.end(); }

};

int main() {

    Test foo;

    foo.insert(23);
    foo.insert(40);

    for (Test::It i = foo.begin(); i != foo.end(); ++i)
     cout << *i << endl;

    return 0;
}

You can now change the Collection-typedef without having to change anything else. (Note: If you make Collection public, the user will be able to refer the the type you used explicitly)

Dario
If you are return const_iterators. Then make the begin() and end() const methods.
Martin York
Yep - Edited that
Dario
+2  A: 

From your description I think the short answer is no.

In general when I'm creating some form of collection like this I would normally use a typedef to specify the container that I'm using:

class Object {
   typedef std::list<int> Cont;
   typedef Cont::iterator iterator;
   typedef Cont::const_iterator const_iterator;

   // ....
};

All client code refers to "Object::Cont" etc. and so as long as clients only use the general features of containers, they will not need to change if the container changes.

If you cannot change your API now, then I think your solution is pretty good, however, depending on the data that you have, if you do a lot of inserts that tend to be unique, then it may be more efficient to continue using the list and only remove duplicates at the end:

void foo (std::list<int> & list) {

  // ... fill the list

  list.sort ();
  list.unique (); 
}
Richard Corden
+2  A: 

No interface exists. Instead, you would typically use templates, and simply say "I don't care what type it is, as long as it behaves as a container".

Assuming your function looks like this:

std::list<int> DoStuff()

it can be called like this:

template <typename container_type>
void caller() {
  container_type result = DoStuff();
}

Only the first function has to be changed if you decide to return a set instead. The calling function doesn't really care (as long as you don't rely on the specifics of a list, of course).

If you post a bit more sample code, we might be better able to suggest how it should be done in C++.

jalf
+4  A: 

The concept of a container is enbodied by iterators.
As you have seen hard coding a specific type of container is probably not what you want. So make your class return iterators. You can then re-use the conatiners iterators.

class MyClass
{
    private:
        typedef  std::list<int>            Container;
    public:
        typedef  Container::iterator       iterator;
        typedef  Container::const_iterator const_iterator; 


        iterator        begin()        {return myData.begin();}
        const_iterator  begin() const  {return myData.begin();}

        iterator        end()          {return myData.end();}
        const_iterator  end()   const  {return myData.end();}

    private:
        Container   myData;
};

Now when you change the Container type from std::list to std::set nobody needs to know.
Also by using the standard names that other containers use your class starts to look like any other container from the STL.

Note: A method that returns a const_iterator should be a const method.

Martin York