問題描述
C ++中集合/容器的接口/超類 (Interface/Superclass for Collections/Containers in c++)
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.
參考解法
方法 1:
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.
方法 2:
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)
方法 3:
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++.
方法 4:
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 ();
}
(by Janusz、Martin York、Dario、jalf、Richard Corden)