2009-08-09 4 views
2

Допустим, у меня есть связанный список с кучей разных данных.Существует ли общий способ перебора определенной переменной в группе объектов?

class Node 
{ 
public: 
    Node* next; 
    AAA dataA; 
    BBB dataB; 
    CCC dataC; 
}; 

Есть ли способ я сделать один итератор, который будет выполнять итерацию над любой переменной Уточняю (вместо того, чтобы три отдельные для каждой переменной). Я понимаю, что итератор мог использовать шаблоны, чтобы он перебирал типы AAA, BBB или CCC, но я не знаю, как я могу указать, какую переменную возвращать.

ответ

1

Я думаю, что нашел способ сделать практически то, что я хочу, исходя из предложения rstevens. Я посмотрел некоторые вещи на указателях членов класса и был в состоянии пропустить Посредник класс аксессора делая это:

template <typename T> 
class iterator 
{ 
private: 
    Node *current; 
    T Node::*var; 

public: 
    iterator() 
     : current(NULL), var(NULL) {} 

    iterator(Node *start, T Node::*var) 
     : current(start), var(var) 
    { 
    } 

    typename T &operator *() const 
    { 
     return current->*var; 
    } 

    bool end() const 
    { 
     return (current == NULL); 
    } 

    iterator &operator++() 
    { 
     if (current) 
      current = current->next; 
     return *this; 
    } 
}; 

А потом я изменил узел, чтобы иметь удобные функции, чтобы сделать итераторы:

class Node 
{ 
public:  
    Node* next; 
    AAA dataA; 
    BBB dataB; 
    CCC dataC; 

    typedef iterator<AAA> AIter; 
    typedef iterator<BBB> BIter; 
    typedef iterator<CCC> CIter; 

    AIter getAIter() 
    { 
     return AIter(this, &Node::dataA); 
    } 

    BIter getBIter() 
    { 
     return BIter(this, &Node::dataB); 
    } 

    CIter getCIter() 
    { 
     return CIter(this, &Node::dataC); 
    } 
}; 

Итак, теперь я могу сделать это, чтобы легко перебирать каждый член данных моего класса:

for (Node::CIter iter = n1.getCIter(); !iter.end(); ++iter) 
{ 
    // tada! 
} 
+0

Мне не нравится это решение, потому что оно навязчиво для класса Node. – Sogartar

+0

Не стесняйтесь использовать его в своем собственном коде. :) Он решил любую проблему, которую я имел 3 года назад, этого было достаточно для меня. – Alex

0

Невозможно создать шаблон на C++, который сам по себе будет перебирать элементы типа. Для этого требуется некоторая помощь в классе либо в форме специализированных шаблонов, специальных методов и т. Д.

Шаблон сам по себе будет довольно сложным и потребует много работы по настройке. Причина в том, что при заданном типе итерации шаблон должен иметь дело с N другими типами. А именно, тип возвращаемых членов.

Нельзя сказать, что это невозможно сделать (это может быть), просто это сложнее простого метода шаблонов.

0

Я сомневаюсь, что вы можете использовать шаблоны для автоматического выбора правильной переменной для возврата, за исключением указания трех специализированных шаблонов, которые будут такими же, как определение трех классов. Однако вы могли бы создать один класс итератора тремя различными способами для возврата данныхA, dataB или dataC соответственно (вместо оператора *()).

+0

Могу ли я сделать некоторые сложные шаблоны работы и указать метод или переменную-геттер? Как MyIterator или MyIterator или что-то подобное? – Alex

2

Возможное решение разделить итератор и доступ в отдельные классы:

класс

итератора, который инкапсулирует доступ к данным с помощью аргумента шаблона:

template <typename Access> 
class iterator 
{ 
private: 
    Node *current; 

public: 
    iterator(Node *start) 
    : current(start) 
    { 
    } 

    typename Access::typeof &operator *() const 
    { 
    return Access::access(*current); 
    } 

    bool end() const 
    { 
    return (current == NULL); 
    } 

    iterator &operator++() 
    { 
    if (current != NULL) 
    { 
     current = current->Next; 
    } 
    } 

    // ... other useful operators/methods 
}; 

классов для доступа к различным данным поля. Таковыми могут быть использованы в качестве параметров шаблона в классе итератора:

class AccessDataA 
{ 
public: 
    typedef AAA typeof; 
    static AAA &access(Node &node) 
    { 
    return node.dataA; 
    } 
}; 

class AccessDataB 
{ 
public: 
    typedef BBB typeof; 
    static BBB &access(Node &node) 
    { 
    return node.dataB; 
    } 
}; 

class AccessDataC 
{ 
public: 
    typedef CCC typeof; 
    static CCC &access(Node &node) 
    { 
    return node.dataC; 
    } 
}; 

Пример использования:

Node *start = ...; 

// Loop over B: 
for (iterator<AccessB> it(start); it++; !it.end()) 
{ 
    // ... *it ... 
} 

// Loop over C: 
for (iterator<AccessC> it(start); it++; !it.end()) 
{ 
    // ... *it ... 
} 

Одним из улучшений можно было бы добавить STL совместимы семантический поэтому ваш список и итераторы могут быть использованы в методах STL, как станд :: for_each.

0

Ваш вопрос действительно только вопрос детали.

Вы можете сделать адаптер итератора, который поступил так же, как итерация по коллекции AAA, но фактически выполняла итерацию по коллекции Node. Однако это может быть не лучшим решением вашей основной проблемы.

Я предполагаю, что у вас есть какое-то действие, которое вы хотите выполнить на каждом члене aaa. Предположим, что это был такой функтор.

struct DoAAAAction 
{ 
    void operator()(AAA& a); 
}; 

Это, вероятно, легче адаптировать действие действовать на Node.

template<class Action> 
class DataA_ActionAdapter 
{ 
public: 
    DataA_ActionAdapter(Action aa) : a(aa) {} 
    void operator()(Node& n) { a(n.dataAAA); } 
private: 
    Action a; 
}; 

Это позволяет использовать стандартные алгоритмы на Node итераторы.

template<class NodeIterator, class AAAAction> 
void TestAAA(NodeIterator first, NodeIterator last, AAAAction aaaa) 
{ 
    std::for_each(first, last, DataA_ActionAdapter<AAAAction>(aaaa)); 
} 
0

Если я вас правильно понял, вы хотите перебрать dataA, dataB и Datac - так это означает, что AAA, BBB и CCC всех одни и тот же базовый тип (или, по меньшей мере, сходные характеристики). Почему бы просто не сохранить их в std :: vector или std :: set?

Примечание: AAA, BBB и CCC являются производным от NodeType

class Node 
{ 
public: 
    Node() 
    { 
     dataNodes.push_back(AAA()); 
     dataNodes.push_back(BBB()); 
     dataNodes.push_back(CCC()); 
    } 

    // AAA dataA; 
    // BBB dataB; 
    // CCC dataC; 

    std::vector <NodeType> dataNodes; 

    std::vector <NodeType>::iterator begin() 
    { 
     return dataNodes.begin(); 
    } 

    std::vector <NodeType>::iterator end() 
    { 
     return dataNodes.end(); 
    } 
}; 
3

Лучшего способом я нашел, чтобы сделать это с boost bind и boost transform_iterator

Сначала вам нужно сбор объектов узла и итератор, который будет перемещаться по коллекции. Для краткости в моем примере я буду использовать std :: list.

#include <boost/bind.hpp> 
#include <boost/iterator/transform_iterator.hpp> 
using boost; 

struct FunctionAAA 
{ 
    void operator() (const AAA& x) 
    {} 
}; 

struct FunctionBBB 
{ 
    void operator() (const BBB& x) 
    {} 
}; 

typedef std::list<Node> NodeList; 
NodeList collection; 
std::foreach (
    make_transform_iterator (collection->begin(), bind (&Node::dataA, _1)), 
    make_transform_iterator (collection->end(), bind (&Node::dataA, _1)), 
    FunctionAAA()); 

std::foreach (
    make_transform_iterator (collection->begin(), bind (&Node::dataB, _1)), 
    make_transform_iterator (collection->end(), bind (&Node::dataB, _1)), 
    FunctionBBB()); 
Смежные вопросы