Другое основное отличие состоит в том, что переменные таблицы не имеют статистики столбцов, в отличие от временных таблиц. Это означает, что оптимизатор запросов не знает, сколько строк находится в табличной переменной (он угадывает 1), что может привести к генерации крайне неоптимальных планов, если переменная таблицы на самом деле имеет большое количество строк.
Концепция контейнера воплощается в итераторах.
Как вы видели, жесткое кодирование конкретного типа контейнера, вероятно, не то, что вам нужно. Так что сделайте так, чтобы ваш класс возвращал итераторы. Затем вы можете повторно использовать итераторы conatiners.
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;
};
Теперь, когда вы меняете тип контейнера с std :: list на std :: set, никому не нужно знать.
Также при использовании стандартных имен, которые используют другие контейнеры, ваш класс начинает выглядеть как любой другой контейнер из STL.
Примечание: метод, возвращающий const_iterator, должен быть методом const.
Вся стандартная библиотека C ++, включая ее контейнеры, - в отличие от Java - не интерфейс (наследование, полиморфизм), а основана на шаблонах (ради эффективности).
Вы можете создать полиморфная оболочка вокруг вашей коллекции, но это не способ C ++.
Самое простое решение - просто упростить программу с помощью некоторых псевдонимов типов:
#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;
}
Теперь вы можете изменить Collection
-typedef без необходимости ничего менять. (Примечание: если вы сделаете Коллекцию
общедоступной, пользователь сможет ссылаться на тип, который вы явно использовали)
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 ();
}
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++.