Я изучаю использование шаблона посетителя. В некоторых примерах, которые я вижу, предлагается использовать функцию accept (Visitor) в каждом подклассе Element. Является ли идея этой функции просто так, чтобы посетитель мог посетить коллекцию, которая содержит полиморфные типы? В этом коде я использую посетителя для двух типов накопления, и для него не требуется accept ().
#include <iostream>
#include <vector>
class IntItem
{
public:
IntItem(const int a) : IntData(a){}
int IntData;
};
class DoubleItem
{
public:
DoubleItem(const double a) : DoubleData(a){}
double DoubleData;
};
class Visitor
{
public:
virtual void Visit(IntItem &item) = 0;
virtual void Visit(DoubleItem &item) = 0;
};
class SumAccumulator : public Visitor
{
public:
SumAccumulator() : Sum(0) {}
void Visit(IntItem &item)
{
Sum += item.IntData;
}
void Visit(DoubleItem &item)
{
Sum += item.DoubleData;
}
double Sum;
};
class AverageAccumulator : public Visitor
{
public:
AverageAccumulator() : Average(0), Counter(0) {}
void Visit(IntItem &item)
{
Counter++;
Average = (Counter - 1) * Average + item.IntData;
Average /= Counter;
}
void Visit(DoubleItem &item)
{
Counter++;
Average = (Counter - 1) * Average + item.DoubleData;
Average /= Counter;
}
int Counter;
double Average;
};
class IntCollection
{
public:
void Visit(Visitor &visitor)
{
for(unsigned int i = 0; i < IntItems.size(); ++i)
{
visitor.Visit(IntItems[i]);
}
}
void AddIntItem(const IntItem& item)
{
IntItems.push_back(item);
}
private:
std::vector<IntItem> IntItems;
};
class DoubleCollection
{
public:
void Visit(Visitor &visitor)
{
for(unsigned int i = 0; i < DoubleItems.size(); ++i)
{
visitor.Visit(DoubleItems[i]);
}
}
void AddDoubleItem(const DoubleItem& item)
{
DoubleItems.push_back(item);
}
private:
std::vector<DoubleItem> DoubleItems;
};
int main(int argc, char *argv[])
{
/////// Ints ////////
IntCollection intCollection;
for(unsigned int i = 0; i < 4; ++i)
{
intCollection.AddIntItem(IntItem(i));
}
SumAccumulator intSumAccumulator;
intCollection.Visit(intSumAccumulator);
std::cout << "int sum: " << intSumAccumulator.Sum << std::endl;
AverageAccumulator intAverageAccumulator;
intCollection.Visit(intAverageAccumulator);
std::cout << "int average: " << intAverageAccumulator.Average << std::endl;
/////// Doubles ////////
DoubleCollection doubleCollection;
for(unsigned int i = 0; i < 4; ++i)
{
doubleCollection.AddDoubleItem(DoubleItem(static_cast<double>(i) + .1));
}
SumAccumulator doubleSumAccumulator;
doubleCollection.Visit(doubleSumAccumulator);
std::cout << "double sum: " << doubleSumAccumulator.Sum << std::endl;
AverageAccumulator doubleAverageAccumulator;
doubleCollection.Visit(doubleAverageAccumulator);
std::cout << "double average: " << doubleAverageAccumulator.Average << std::endl;
return 0;
}
В этом коде я использую accept (), и единственное отличие состоит в том, что контейнер может содержать объекты разных типов в одном и том же контейнере:
#include <iostream>
#include <string>
#include <vector>
class IntElement;
class DoubleElement;
class Visitor
{
public:
virtual void visit(IntElement *e) = 0;
virtual void visit(DoubleElement *e) = 0;
};
class Element
{
public:
virtual void accept(class Visitor &v) = 0;
};
class IntElement: public Element
{
public:
IntElement(int i) : IntData(i){}
/*virtual*/void accept(Visitor &v)
{
v.visit(this);
}
int IntData;
};
class DoubleElement: public Element
{
public:
DoubleElement(double d) : DoubleData(d){}
/*virtual*/void accept(Visitor &v)
{
v.visit(this);
}
double DoubleData;
};
class SumVisitor: public Visitor
{
public:
SumVisitor() : Sum(0){}
/*virtual*/void visit(IntElement *e)
{
Sum += e->IntData;
}
/*virtual*/void visit(DoubleElement *e)
{
Sum += e->DoubleData;
}
double Sum;
};
class AverageVisitor: public Visitor
{
public:
AverageVisitor() : Counter(0) , Average(0){}
/*virtual*/void visit(IntElement *e)
{
Counter++;
Average = (Counter - 1) * Average + e->IntData;
Average /= Counter;
}
/*virtual*/void visit(DoubleElement *e)
{
Counter++;
Average = (Counter - 1) * Average + e->DoubleData;
Average /= Counter;
}
double Average;
int Counter;
};
int main()
{
std::vector<Element*> elements;
elements.push_back(new IntElement(0));
elements.push_back(new IntElement(1));
elements.push_back(new DoubleElement(2));
elements.push_back(new DoubleElement(3));
SumVisitor sumVisitor;
AverageVisitor averageVisitor;
for (int i = 0; i < elements.size(); i++)
{
elements[i]->accept(sumVisitor);
elements[i]->accept(averageVisitor);
}
std::cout << "sum: " << sumVisitor.Sum << std::endl;
std::cout << "average: " << averageVisitor.Average << std::endl;
}
Правильно ли это различие? То есть, если я планирую иметь только однородные контейнеры, мне не нужно реализовывать функции accept?