-
Notifications
You must be signed in to change notification settings - Fork 2
ООП Лекция 14. Паттерны поведения.
Паттернов поведения колоссальное количество:
Что такое стратегия? Стратегию можно определить, как выбор подхода решению к чему-либо, какой-либо задачи. В самом названии понятно, что мы задаем стратегию, а потом когда-то будем выполнять данную стратегию решения задачи. Исторически, как он появился иначе, в процедурных языках у нас подход Callback
вызовов, так называемый указатель на функцию. По причине чего могли менять алгоритм. Например, у нас функция сортировки, и одном из его параметров передаем указатель на функцию сравнения чего-либо, тем самым меняем алгоритм сравнения в алгоритме сортировки.
В данном случае, язык C++ ООП и поэтому мы функцию оборачиваем в класс, по существу Стратегия - это класс, содержащий один метод. Сама стратегия рассматривается, как абстрактное понятие, то есть создаем конкретную Стратегию от базового класса Стратегии.
Нам во время выполнения надо менять реализацию какого-либо метода. Мы можем делать производные классы с разными реализациями и осуществлять "миграцию" между классами во врем выполнения - это неудобно, ибо мы начинаем работать с конкретными типами (классами)
Клиент может установить для нашего класса конкретную стратегию (алгоритм) и, работая с объектом, он будет вызывать этот конкретный алгоритм. Во время работы мы можем этот алгоритм поменять.
Рассмотрим сравнение этого паттерна с Структурными:
Во время выполнения программы мы можем менять стратегию.
Чтобы не плодить иерархию, мы можем вынести что-то в стратегию, тем самым это паттерн немного схож с декоратором. Но большое сходство он имеет с паттерном Мост
, и является вырожденным Мостом. Реализация не всего, а лишь какого-то метода. При этом стратегию могут использовать объекты не являются родственными.
Проблема адаптера - пересечение интерфейсов и решаем дублированием кода, когда Стратегия эту проблему решает без дублирования кода, то есть можно ее использовать с адаптером, тем самым решаем проблему пересечения интерфейсом без дублирования кода.
- алгоритмы "Стратегии" могут иметь пересечение (опять дублирование кода) => решение разбивать стратегию, делать несколько стратегий, можно создавать композиция стратегий, то есть использовать не одну стратегию, а сразу несколько;
- "Стратегия" должна работать с полями данных объекта. А мы не всегда можем свести к базовым данным.
Объект держит указатель на стратегию. Мы один раз установили стратегию, можем, конечно, её поменять. Вызывая для объекта, вызывается та стратегия, которая нас интересует.
# include <iostream>
# include <memory>
# include <vector>
using namespace std;
class Strategy
{
public:
virtual ~Strategy() = default;
virtual void algorithm() = 0;
};
class ConStrategy1 : public Strategy
{
public:
virtual void algorithm() override { cout << "Algorithm 1;" << endl; }
};
class ConStrategy2 : public Strategy
{
public:
virtual void algorithm() override { cout << "Algorithm 2;" << endl; }
};
class Context
{
protected:
unique_ptr<Strategy> strategy;
public:
explicit Context(unique_ptr<Strategy> ptr = make_unique<ConStrategy1>())
: strategy(move(ptr)) {}
virtual ~Context() = default;
virtual void algorithmStrategy() = 0;
};
class Client1 : public Context
{
public:
using Context::Context;
virtual void algorithmStrategy() override { strategy->algorithm(); }
};
void main()
{
// shared_ptr<Context> obj = make_shared<Client1>();
shared_ptr<Context> obj = make_shared<Client1>(make_unique<ConStrategy2>());
obj->algorithmStrategy();
}
При выполнении мы передаем, какую стратегию хотим использовать. Мы не держим указатель, а устанавливаем при работе.
# include <iostream>
# include <memory>
# include <vector>
using namespace std;
class Strategy
{
public:
virtual ~Strategy() = default;
virtual void algorithm() = 0;
};
class ConStrategy1 : public Strategy
{
public:
virtual void algorithm() override { cout << "Algorithm 1;" << endl; }
};
class ConStrategy2 : public Strategy
{
public:
virtual void algorithm() override { cout << "Algorithm 2;" << endl; }
};
class Context
{
public:
virtual void algorithmStrategy(shared_ptr<Strategy> strategy) = 0;
};
class Client1 : public Context
{
public:
virtual void algorithmStrategy(shared_ptr<Strategy> strategy = make_shared<ConStrategy1>()) override
{ strategy->algorithm(); }
};
void main()
{
shared_ptr<Context> obj = make_shared<Client1>();
shared_ptr<Strategy> strategy = make_shared<ConStrategy2>();
obj->algorithmStrategy(strategy);
}
Вариант со статическим полиморфизмом. Статический полиморфизм - на этапе компиляции происходит связывание, не можем выбрать на этапе выполнения.
Единственный плюс этого варианта - быстрее.
# include <iostream>
# include <memory>
# include <vector>
using namespace std;
class Strategy
{
public:
virtual ~Strategy() = default;
virtual void algorithm() = 0;
};
class ConStrategy1 : public Strategy
{
public:
virtual void algorithm() override { cout << "Algorithm 1;" << endl; }
};
class ConStrategy2 : public Strategy
{
public:
virtual void algorithm() override { cout << "Algorithm 2;" << endl; }
};
template <typename TStrategy = ConStrategy1>
class Context
{
private:
unique_ptr<TStrategy> strategy;
public:
Context() : strategy(make_unique<TStrategy>()) {}
void algorithmStrategy() { strategy->algorithm(); }
};
int main()
{
// shared_ptr<Context<>> obj = make_shared<Context<>>();
using Client = Context<ConStrategy2>;
shared_ptr<Client> obj = make_shared<Client>();
obj->algorithmStrategy();
}
# include <iostream>
# include <memory>
# include <initializer_list>
using namespace std;
class Strategy;
class Array
{
public:
Array(initializer_list<double> list);
void sort(shared_ptr<Strategy> algorithm);
const double& operator [](int index) const { return this->arr[index]; }
unsigned size() const { return count; }
private:
shared_ptr<double[]> arr;
unsigned count;
};
class Strategy
{
public:
virtual void algorithmSort(shared_ptr<double[]> ar, unsigned cnt) = 0;
};
#pragma region Array methods
Array::Array(initializer_list<double> list)
{
this->count = list.size();
this->arr = shared_ptr<double[]>(new double[this->count]);
unsigned i = 0;
for (auto elem : list)
arr[i++] = elem;
}
void Array::sort(shared_ptr<Strategy> algorithm)
{
algorithm->algorithmSort(this->arr, this->count);
}
#pragma endregion
template <typename TComparison>
class BustStrategy : public Strategy
{
public:
virtual void algorithmSort(shared_ptr<double[]> ar, unsigned cnt) override
{
for (int i = 0; i < cnt - 1; i++)
for (int j = i + 1; j < cnt; j++)
{
if (TComparison::compare(ar[i], ar[j]) > 0)
swap(ar[i], ar[j]);
}
}
};
template <typename Type>
class Comparison
{
public:
static int compare(const Type& elem1, const Type& elem2) { return elem1 - elem2; }
};
ostream& operator <<(ostream& os, const Array& ar)
{
for (int i = 0; i < ar.size(); i++)
os << " " << ar[i];
return os;
}
void main()
{
using TStrategy = BustStrategy<Comparison<double>>;
shared_ptr<Strategy> strategy = make_shared<TStrategy>();
Array ar{ 8., 6., 4., 3., 2., 7., 1. };
ar.sort(strategy);
cout << ar << endl;
}
Команда - объект, который держит указатель на объект, указатель на метод, который должен быть вызван для этого объекта и данные, которые нужны для этого метода.
Но хотелось бы отвязаться от объекта. Перенести решение, может ли быть обработан запрос, на объект, который принимает запрос.
Возможны разные запросы (загрузить, повернуть, перенести и подобное). Идея такая: обернуть каждый запрос в отдельный класс (класс может быть как простой, так и составной)
При этом передаем данные для выполнения команды, и передаем их в Callback, при этом жестко завязаны к методу, то есть прелесть можем все скрыть, можем сделать один метод execute
, будут передаваться метод по указателю на метод.
- Уменьшается зависимость между объектами, не нужно держать связь
- Команду можем выполнить не сразу, а через время, можно сформировать очередь
- Если добавить к команде композит, то можно формировать сложные команды из нескольких команд
# include <iostream>
# include <memory>
# include <vector>
# include <initializer_list>
using namespace std;
class Command
{
public:
virtual ~Command() = default;
virtual void execute() = 0;
};
template <typename Reseiver>
class SimpleCommand : public Command
{
using Action = void(Reseiver::*)();
using Pair = pair<shared_ptr<Reseiver>, Action>;
private:
Pair call;
public:
SimpleCommand(shared_ptr<Reseiver> r, Action a) : call(r, a) {}
virtual void execute() override { ((*call.first).*call.second)(); }
};
class CompoundCommand : public Command
{
using VectorCommand = vector<shared_ptr<Command>>;
private:
VectorCommand vec;
public:
CompoundCommand(initializer_list<shared_ptr<Command>> lt);
virtual void execute() override;
};
# pragma region Methods
CompoundCommand::CompoundCommand(initializer_list<shared_ptr<Command>> lt)
{
for (auto elem : lt)
vec.push_back(elem);
}
void CompoundCommand::execute()
{
for (auto com : vec)
com->execute();
}
# pragma endregion
class Object
{
public:
void run() { cout << "Run method;" << endl; }
};
int main()
{
shared_ptr<Object> obj = make_shared<Object>();
shared_ptr<Command> command = make_shared<SimpleCommand<Object>>(obj, &Object::run);
command->execute();
shared_ptr<Command> complex(new CompoundCommand
{
make_shared<SimpleCommand<Object>>(obj, &Object::run),
make_shared<SimpleCommand<Object>>(obj, &Object::run)
});
complex->execute();
}
# include <iostream>
# include <memory>
using namespace std;
template <typename Reseiver>
class Command
{
public:
virtual ~Command() = default;
virtual void execute(shared_ptr<Reseiver>) = 0;
};
template <typename Reseiver>
class SimpleCommand : public Command<Reseiver>
{
using Action = void(Reseiver::*)();
private:
Action act;
public:
SimpleCommand(Action a) : act(a) {}
virtual void execute(shared_ptr<Reseiver> r) override { ((*r).*act)(); }
};
class Object
{
public:
virtual void run() = 0;
};
class ConObject : public Object
{
public:
virtual void run() override { cout << "Run method;" << endl; }
};
int main()
{
shared_ptr<Command<Object>> command = make_shared<SimpleCommand<Object>>(&Object::run);
shared_ptr<Object> obj = make_shared<ConObject>();
command->execute(obj);
}
Строится список из обработчиков. Запрос не связан с обработчиком. Обработчики принимают запрос и смотрят, могут ли они обработать его. Запрос может жить до тех пор, пока он не будет обработан.
Запрос может быть обработан несколькими обработчиками. Запрос может модифицироваться, обработчик может его обработать изменить и послать его дальше по цепочке. Например, ОС Windows, по нажатию мышки идет обработчик этого клика, и каждый обработчик смотрит его это поле видимости или нет, так при нажатие на окне Canvas, то там что отрисовывается, а на поля меню нет.
Cоздать цепочку обработчиков.
Hadler
определяет общий интерфейс и. задает механизм передачи запроса, а каждый ConHandler1..n
содержат код обработки запросов.
Когда надо использовать?
- один и тот же запрос может выполняться разным способами
- есть четкая последовательность в обработчиках (можем передавать до тех пор, пока не обрабатывается, но важна последовательность)
- на этапе выполнения мы решаем, какие объекты будут в этой цепочке
- Позволяет передавать запрос последовательно по цепочке обработчиков, каждый обработчик сам решает, передавать дальше или нет.
- Новый обработчик добавляется всегда в голову. Не всегда хорошо. Лучше держать обработчики в списке, была возможно передачи и легкой переменчивости.
- В случае изменение объекта, хотелось наблюдать за изменением объекта и реагировать на него (запрос другого рода => запрос на изменение)
# include <iostream>
# include <initializer_list>
# include <memory>
using namespace std;
class AbstractHandler
{
using PtrAbstractHandler = shared_ptr<AbstractHandler>;
protected:
PtrAbstractHandler next;
virtual bool run() = 0;
public:
using Default = shared_ptr<AbstractHandler>;
virtual ~AbstractHandler() = default;
virtual bool handle() = 0;
void add(PtrAbstractHandler node);
void add(PtrAbstractHandler node1, PtrAbstractHandler node2, ...);
void add(initializer_list<PtrAbstractHandler> list);
};
class ConHandler : public AbstractHandler
{
private:
bool condition{ false };
protected:
virtual bool run() override { cout << "Method run;" << endl; return true; }
public:
ConHandler() : ConHandler(false) {}
ConHandler(bool c) : condition(c) { cout << "Constructor;" << endl; }
virtual ~ConHandler() override { cout << "Destructor;" << endl; }
virtual bool handle() override
{
if (!condition) return next ? next->handle() : false;
return run();
}
};
#pragma region Methods
void AbstractHandler::add(PtrAbstractHandler node)
{
if (next)
next->add(node);
else
next = node;
}
void AbstractHandler::add(PtrAbstractHandler node1, PtrAbstractHandler node2, ...)
{
for (Default* ptr = &node1; *ptr; ++ptr)
add(*ptr);
}
void AbstractHandler::add(initializer_list<PtrAbstractHandler> list)
{
for (auto elem : list)
add(elem);
}
#pragma endregion
int main()
{
shared_ptr<AbstractHandler> chain = make_shared<ConHandler>();
chain->add(
{
make_shared<ConHandler>(false),
make_shared<ConHandler>(true),
make_shared<ConHandler>(true),
AbstractHandler::Default()
}
);
cout << "Result = " << chain->handle() << ";" << endl;
}
Часто нам надо какой-либо запрос передавать не одному, многим объектам, и это должно выполняться на этапе выполнения.
Возложить на объект, который изменяется функция оповещения других объектов.
Но возникает вопрос, он должен знать кого оповещать. Значит на тех кто хочет наблюдать, они должны подписаться на эти изменения, то есть издатель должен держать список тех, кто подписался на изменения с ним. Если есть метод подписать, то должен быть и метод отписаться.
Обычно, когда важно наличие подписчиков, издатель держит указатель (shared) на подписчика. Если же не важно наличие подписчиков, издатель держит указатель (weak) на подписчика.
- Издатели не зависят от подписчиков
- Схема гибкая: можно как подписаться, так и подписаться (надо делать iterator)
- Паттерн тяжёлый. Мы должны держать список подписчиков;
- Подписка друг на друга.
- Контроль связей
- Огромный класс, удержания подписчиков.
Решение проблем следующий паттерн
Посредник
.
# include <iostream>
# include <memory>
# include <vector>
using namespace std;
class Subscriber;
using Reseiver = Subscriber;
class Publisher
{
using Action = void(Reseiver::*)();
using Pair = pair<shared_ptr<Reseiver>, Action>;
private:
vector<Pair> callback;
int indexOf(shared_ptr<Reseiver> r);
public:
bool subscribe(shared_ptr<Reseiver> r, Action a);
bool unsubscribe(shared_ptr<Reseiver> r);
void run();
};
class Subscriber
{
public:
virtual ~Subscriber() = default;
virtual void method() = 0;
};
class ConSubscriber : public Subscriber
{
public:
virtual void method() override { cout << "method;" << endl; }
};
#pragma region Methods Publisher
bool Publisher::subscribe(shared_ptr<Reseiver> r, Action a)
{
if (indexOf(r) != -1) return false;
Pair pr(r, a);
callback.push_back(pr);
return true;
}
bool Publisher::unsubscribe(shared_ptr<Reseiver> r)
{
int pos = indexOf(r);
if (pos != -1)
callback.erase(callback.begin() + pos);
return pos != -1;
}
void Publisher::run()
{
cout << "Run:" << endl;
for (auto elem : callback)
((*elem.first).*(elem.second))();
}
int Publisher::indexOf(shared_ptr<Reseiver> r)
{
int i = 0;
for (auto it = callback.begin(); it != callback.end() && r != (*it).first; i++, ++it);
return i < callback.size() ? i : -1;
}
#pragma endregion
int main()
{
shared_ptr<Subscriber> subscriber1 = make_shared<ConSubscriber>();
shared_ptr<Subscriber> subscriber2 = make_shared<ConSubscriber>();
shared_ptr<Publisher> publisher = make_shared<Publisher>();
publisher->subscribe(subscriber1, &Subscriber::method);
if (publisher->subscribe(subscriber2, &Subscriber::method))
publisher->unsubscribe(subscriber1);
publisher->run();
}
Логика связей в отдельный класс. Разгрузим наш объектов от списков связей.
(последний недостаток подписчика-издателя)
Cвязи вынести в отдельный объект, тогда каждый объект будет обращаться к этому отдельному объекту, а он в свою очередь будет искать нужные связи (с кем ему связаться). Позволяет уменьшить количество связей между объектами благодаря перемещению связей в него, в посредник. Так как посредник должен работать со всеми объектами, то он содержит указатели на все эти объекты.
Конкретный медиатор устанавливает, кому передается и сообщение.
- Упрощает используемый объект
- Снимает обязанности
Следующая проблема – изменение интерфейса объектов: если мы используем полиморфизм, мы не можем в производном классе ни сузить, ни расширить интерфейс. Пр. класс должен четко поддерживать базовый класс. Мы также рассматривали паттерн адаптер, с помощью него мы можем добавить какой-то функционал на основе того функционала, который у нас есть. То есть расширять мы умеем. Решение использовать архитектурный паттерн - КМС
# include <iostream>
# include <memory>
# include <list>
# include <vector>
using namespace std;
class Message {}; // Request
class Mediator;
class Colleague
{
private:
weak_ptr<Mediator> mediator;
public:
virtual ~Colleague() = default;
void setMediator(shared_ptr<Mediator> mdr) { mediator = mdr; }
virtual bool send(shared_ptr<Message> msg);
virtual void receive(shared_ptr<Message> msg) = 0;
};
class ColleagueLeft : public Colleague
{
public:
virtual void receive(shared_ptr<Message> msg) override { cout << "Right - > Left;" << endl; }
};
class ColleagueRight : public Colleague
{
public:
virtual void receive(shared_ptr<Message> msg) override { cout << "Left - > Right;" << endl; }
};
class Mediator
{
protected:
list<shared_ptr<Colleague>> colleagues;
public:
virtual ~Mediator() = default;
virtual bool send(const Colleague* coleague, shared_ptr<Message> msg) = 0;
static bool add(shared_ptr<Mediator> mediator, initializer_list<shared_ptr<Colleague>> list);
};
class ConMediator : public Mediator
{
public:
virtual bool send(const Colleague* coleague, shared_ptr<Message> msg) override;
};
#pragma region Methods Colleague
bool Colleague::send(shared_ptr<Message> msg)
{
shared_ptr<Mediator> mdr = mediator.lock();
return mdr ? mdr->send(this, msg) : false;
}
#pragma endregion
#pragma region Methods Mediator
bool Mediator::add(shared_ptr<Mediator> mediator, initializer_list<shared_ptr<Colleague>> list)
{
if (!mediator || list.size() == 0) return false;
for (auto elem : list)
{
mediator->colleagues.push_back(elem);
elem->setMediator(mediator);
}
return true;
}
bool ConMediator::send(const Colleague* colleague, shared_ptr<Message> msg)
{
bool flag = false;
for (auto& elem : colleagues)
{
if (dynamic_cast<const ColleagueLeft*>(colleague) && dynamic_cast<ColleagueRight*>(elem.get()))
{
elem->receive(msg);
flag = true;
}
else if (dynamic_cast<const ColleagueRight*>(colleague) && dynamic_cast<ColleagueLeft*>(elem.get()))
{
elem->receive(msg);
flag = true;
}
}
return flag;
}
#pragma endregion
int main()
{
shared_ptr<Mediator> mediator =make_shared<ConMediator>();
shared_ptr<Colleague> col1 = make_shared<ColleagueLeft>();
shared_ptr<Colleague> col2 = make_shared<ColleagueRight>();
shared_ptr<Colleague> col3 = make_shared<ColleagueLeft>();
shared_ptr<Colleague> col4 = make_shared<ColleagueLeft>();
Mediator::add(mediator, { col1, col2, col3, col4 });
shared_ptr<Message> msg = make_shared<Message>();
col1->send(msg);
col2->send(msg);
}
При проектировании мы должны выделить базовые абстракции и эти базовые абстракции задают интерфейс производных классов, использовать полиморфизм в чистом ввиде, то есть не сужать и не расширять интерфейс базового класса. Если нам необходимо расширить интерфейс, можно использовать паттерн Визитёр. Он позволяет во время выполнения (в отличие от паттерна Адаптера, который решает эту проблему до выполнения) подменить или расширить функционал.
Через адаптер возникает дублирование кода. Адаптер должен расширять интерфейс, чтоб добавить новый функционал, то надо работать с нутром сущности. Возникает проблема с сведением этой связи к базовому классу сущности, расширение реализовывается по-разному.
Методы стратегии объединить в один класс
Чтобы можно было поменять/расширять функционал, а базовом классе добавляем метод accept(Visiter). Соответственно, все производные классы могут подменять этот метод.
- При изменении иерархии, нужно менять код.
- Множество сущностей должно быть постоянным (Например, в 3 лабе, 4 сущности в 3-м пространстве: камеры, сцена, объекты, свет)
- Расширяется иерархия, добавляются новые классы. Проблема связи на уровне базовых классов
- Доступ к полям сущности (дружба)
# include <iostream>
# include <memory>
# include <vector>
using namespace std;
class Circle;
class Rectangle;
class Visitor
{
public:
virtual ~Visitor() = default;
virtual void visit(Circle& ref) = 0;
virtual void visit(Rectangle& ref) = 0;
};
class Shape
{
public:
virtual ~Shape() = default;
virtual void accept(shared_ptr<Visitor> visitor) = 0;
};
class Circle : public Shape
{
public:
virtual void accept(shared_ptr<Visitor> visitor) override { visitor->visit(*this); }
};
class Rectangle : public Shape
{
public:
virtual void accept(shared_ptr<Visitor> visitor) override { visitor->visit(*this); }
};
class ConVisitor : public Visitor
{
public:
virtual void visit(Circle& ref) override { cout << "Circle;" << endl; }
virtual void visit(Rectangle& ref) override { cout << "Rectangle;" << endl; }
};
class Formation
{
public:
static vector<shared_ptr<Shape>> initialization(initializer_list<shared_ptr<Shape>> list)
{
vector<shared_ptr<Shape>> vec;
for (auto elem : list)
vec.push_back(elem);
return vec;
}
};
int main()
{
vector<shared_ptr<Shape>> figure = Formation::initialization(
{ make_shared<Circle>(), make_shared<Rectangle>(), make_shared<Circle>() }
);
shared_ptr<Visitor> visitor = make_shared<ConVisitor>();
for (auto& elem : figure)
elem->accept(visitor);
}
# include <iostream>
# include <memory>
# include <initializer_list>
# include <vector>
using namespace std;
template <typename ...Types>
class Visitor;
template <typename Type>
class Visitor<Type>
{
public:
virtual void visit(Type& t) = 0;
};
template <typename Type, typename ...Types>
class Visitor<Type, Types...> : public Visitor<Types...>
{
public:
using Visitor<Types...>::visit;
virtual void visit(Type& t) = 0;
};
using ShapeVisitor = Visitor<class Figure, class Camera>;
class Point {};
class Shape
{
public:
virtual ~Shape() = default;
Shape(const Point& pnt) : point(pnt) {}
const Point& getPoint() const { return point; }
void setPoint(const Point& pnt) { point = pnt; }
virtual void accept(shared_ptr<ShapeVisitor> v) = 0;
private:
Point point;
};
template <typename Derived>
class Visitable : public Shape
{
using Shape::Shape;
public:
virtual void accept(shared_ptr<ShapeVisitor> v) override
{
v->visit(*static_cast<Derived*>(this));
}
};
class Figure : public Visitable<Figure>
{
using Visitable<Figure>::Visitable;
};
class Camera : public Visitable<Camera>
{
using Visitable<Camera>::Visitable;
};
class DrawVisitor : public ShapeVisitor
{
public:
virtual void visit(Figure& fig) override { cout << "Draws a figure;" << endl; }
virtual void visit(Camera& fig) override { cout << "Draws a camera;" << endl; }
};
class Formation
{
public:
static vector<shared_ptr<Shape>> initialization(initializer_list<shared_ptr<Shape>> list)
{
vector<shared_ptr<Shape>> vec;
for (auto elem : list)
vec.push_back(elem);
return vec;
}
};
int main()
{
Point p;
vector<shared_ptr<Shape>> figure = Formation::initialization(
{ make_shared<Figure>(p), make_shared<Camera>(p), make_shared<Figure>(p) }
);
shared_ptr<ShapeVisitor> visitor = make_shared<DrawVisitor>();
for (auto& elem : figure)
elem->accept(visitor);
}
# include <iostream>
# include <variant>
using namespace std;
class ElementA
{
public:
void methodF() const { cout << "calling ElementA!" << endl; }
};
class ElementB
{
public:
void methodG() const { cout << "calling ElementB!" << endl; }
};
struct Call
{
void operator()(const ElementA& d) { d.methodF(); }
void operator()(const ElementB& d) { d.methodG(); }
};
void main()
{
ElementA dr;
variant<ElementA, ElementB> var(dr);
visit(Call(), var);
}
Этот паттерн имеет много названий. Одно из них - Опекун, Хранитель
Пусть построить какую-то модель, но при проверке и обсчете модель не соответствует результату. И нам понадобилось вернуться на шаг назад к предыдущему состоянию нашего объекта (откат).
Как вариант - хранить те состояния, которые были у объекта. Если возложить эту задачу на объект - он получится тяжелым, и не соответствует принципам ООП, т.е. получается уже несколько обязанностей на сущности.
Выделить эту обязанность другому объекту, задача которого - хранить предыдущие состояния нашего объекта, который, если нужно, позволит нам вернуться к какому-либо предыдущему состоянию.
Memento - снимок объекта в какой-то момент времени. Опекун отвечает за хранение этих снимков и по возможности вернуться к предыдущему состоянию объекта.
Позволяет не грузить сам класс задачей сохранять предыдущие состояния.
Опекуном надо управлять. Он наделал снимков, а они нам не нужны. Кто-то должен их очищать. Какой механизм очистки? Много памяти тратится.
# include <iostream>
# include <memory>
# include <list>
using namespace std;
class Memento;
class Caretaker
{
public:
unique_ptr<Memento> getMemento();
void setMemento(unique_ptr<Memento> memento);
private:
list<unique_ptr<Memento>> mementos;
};
class Originator
{
public:
Originator(int s) : state(s) {}
const int getState() const { return state; }
void setState(int s) { state = s; }
std::unique_ptr<Memento> createMemento() { return make_unique<Memento>(*this); }
void restoreMemento(std::unique_ptr<Memento> memento);
private:
int state;
};
class Memento
{
friend class Originator;
public:
Memento(Originator o) : originator(o) {}
private:
void setOriginator(Originator o) { originator = o; }
Originator getOriginator() { return originator; }
private:
Originator originator;
};
#pragma region Methods Caretaker
void Caretaker::setMemento(unique_ptr<Memento> memento)
{
mementos.push_back(move(memento));
}
unique_ptr<Memento> Caretaker::getMemento() {
unique_ptr<Memento> last = move(mementos.back());
mementos.pop_back();
return last;
}
#pragma endregion
#pragma region Method Originator
void Originator::restoreMemento(std::unique_ptr<Memento> memento)
{
*this = memento->getOriginator();
}
#pragma endregion
int main()
{
auto originator = make_unique<Originator>(1);
auto caretaker = make_unique<Caretaker>();
cout << "State = " << originator->getState() << endl;
caretaker->setMemento(originator->createMemento());
originator->setState(2);
cout << "State = " << originator->getState() << endl;
caretaker->setMemento(originator->createMemento());
originator->setState(3);
cout << "State = " << originator->getState() << endl;
caretaker->setMemento(originator->createMemento());
originator->restoreMemento(caretaker->getMemento());
cout << "State = " << originator->getState() << endl;
originator->restoreMemento(caretaker->getMemento());
cout << "State = " << originator->getState() << std::endl;
originator->restoreMemento(caretaker->getMemento());
cout << "State = " << originator->getState() << std::endl;
}
Можем объединять в одни класс этапы выполнения какого-либо метода.
Этот паттерн является скелетом какого-либо метода. Мы любую задачу разбиваем на этапы - формируем шаги, которые мы выполняем для того, чтобы то, что мы получили на входе, преобразовать в результат, который нам нужен.
# include <iostream>
using namespace std;
class AbstractClass
{
public:
void templateMethod()
{
primitiveOperation();
concreteOperation();
hook();
}
protected:
virtual void primitiveOperation() = 0;
void concreteOperation() { cout << "concreteOperation;" << endl; }
virtual void hook() { cout << "hook Base;" << endl; }
};
class ConClassA : public AbstractClass
{
protected:
virtual void primitiveOperation() override { cout << "primitiveOperation A;" << endl; }
};
class ConClassB : public AbstractClass
{
protected:
virtual void primitiveOperation() override { cout << "primitiveOperation B;" << endl; }
void hook() { cout << "hook B;" << endl; }
};
int main()
{
ConClassA ca;
ConClassB cb;
ca.templateMethod();
cb.templateMethod();
}
Объект может находиться в каких-то состояниях, и каждое состояние формировать как класс, который отвечает за какое-то определенное состояние.
Вот в примере ниже возможно три состояние: остановлен, играет и находится в режиме паузы. Мы рассматриваем разные переходи из этих состояний, есть действие play и нажали на паузу. Есть класс, который определяет состояние. Он один, но лучше, конечно, для каждого класса реализовывать свое. И вообще тут пример ужасный по словам Тассова. Приведен просто как пример.
# include <iostream>
# include <memory>
# include <string>
using namespace std;
class MusicPlayerState;
enum class State
{
ST_STOPPED, ST_PLAYING, ST_PAUSED
};
class MusicPlayer
{
public:
explicit MusicPlayer(MusicPlayerState* ptr) : pState(ptr) {}
virtual ~MusicPlayer() = default;
void Play();
void Pause();
void Stop();
void SetState(State state);
private:
unique_ptr<MusicPlayerState> pState;
};
class MusicPlayerState
{
public:
MusicPlayerState(string nm) : name(nm) {}
virtual ~MusicPlayerState() = 0;
virtual void Play(MusicPlayer& player) {}
virtual void Pause(MusicPlayer& player) {}
virtual void Stop(MusicPlayer& player) {}
string GetName() { return name; }
private:
string name;
};
MusicPlayerState::~MusicPlayerState() = default;
class PlayingState : public MusicPlayerState {
public:
PlayingState() : MusicPlayerState(string("Playing")) {}
virtual ~PlayingState() = default;
virtual void Pause(MusicPlayer& player) override { player.SetState(State::ST_PAUSED); }
virtual void Stop(MusicPlayer& player) override { player.SetState(State::ST_STOPPED); }
};
class PausedState : public MusicPlayerState {
public:
PausedState() : MusicPlayerState(string("Paused")) {}
virtual ~PausedState() = default;
virtual void Play(MusicPlayer& player) override { player.SetState(State::ST_PLAYING); }
virtual void Stop(MusicPlayer& player) override { player.SetState(State::ST_STOPPED); }
};
class StoppedState : public MusicPlayerState {
public:
StoppedState() : MusicPlayerState(string("Stopped")) {}
virtual ~StoppedState() = default;
virtual void Play(MusicPlayer& player) override { player.SetState(State::ST_PLAYING); }
};
#pragma region Methods MusicPlayer
void MusicPlayer::Play() { pState->Play(*this); }
void MusicPlayer::Pause() { pState->Pause(*this); }
void MusicPlayer::Stop() { pState->Stop(*this); }
void MusicPlayer::SetState(State state)
{
cout << "changing from " << pState->GetName() << " to ";
if (state == State::ST_STOPPED)
{
pState.reset(new StoppedState());
}
else if (state == State::ST_PLAYING)
{
pState.reset(new PlayingState());
}
else
{
pState.reset(new PausedState());
}
cout << pState->GetName() << " state" << endl;
}
#pragma endregion
int main()
{
MusicPlayer player(new StoppedState());
player.Play();
player.Pause();
player.Stop();
}
Не сколько паттерн поведения, сколько шаблон, который нам дает возможность формализовать свойства. В современных языках есть понятие свойство. Предположим, у нас есть какой то класс, и у его объекта есть свойство V.
A objl
obj V = 2;
int i = obj.V;
(В реальном мире такого доступа быть не должно). Доступ осуществляется через методы, один устанавливает - set(), другой возвращает - get(). Если мы будем рассматривать V как открытый член, но не простое данное... Если рассматривать V как объект, мы для него должны определить V как оператор присваивания.
Нам нужен класс, в котором есть перегруженный оператор = и оператор приведения типа. Этот перегруженный оператор равно должен вызывать метод установки сет, а этот гет.
Удобно создать шаблон свойства. Первый параметр - тип объекта для которого создается шаблон, а второй параметр - тип объекта к которому приводится и ли инициализируется значение. В данном случае это целый тип. Getter - метод класса который возвращает Type, а Setter - установка для вот этого объекта.
# include <iostream>
# include <memory>
using namespace std;
template <typename Owner, typename Type>
class Property
{
using Getter = Type (Owner::*)() const;
using Setter = void (Owner::*)(const Type&);
private:
Owner* owner;
Getter methodGet;
Setter methodSet;
public:
Property() = default;
Property(Owner* const owr, Getter getmethod, Setter setmethod) : owner(owr), methodGet(getmethod), methodSet(setmethod) {}
void init(Owner* const owr, Getter getmethod, Setter setmethod)
{
owner = owr;
methodGet = getmethod;
methodSet = setmethod;
}
operator Type() { return (owner->*methodGet)(); } // Getter
void operator=(const Type& data) { (owner->*methodSet)(data); } // Setter
};
class Object
{
private:
double value;
public:
Object(double v) : value(v) { Value.init(this, &Object::getValue, &Object::setValue); }
double getValue() const { return value; }
void setValue(const double& v) { value = v; }
Property<Object, double> Value;
};
int main()
{
Object obj(5.);
cout << "value = " << obj.Value << endl;
obj.Value = 10.;
cout << "value = " << obj.Value << endl;
unique_ptr<Object> ptr = make_unique<Object>(15.);
cout << "value =" << ptr->Value << endl;
obj = *ptr;
obj.Value = ptr->Value;
}
# include <iostream>
using namespace std;
struct ReadOnly_tag {};
struct WriteOnly_tag {};
struct ReadWrite_tag {};
template <typename Owner, typename Type, typename Access = ReadWrite_tag>
class Property
{
using Getter = Type (Owner::*)() const;
using Setter = void (Owner::*)(const Type&);
private:
Owner* owner;
Getter methodGet;
Setter methodSet;
public:
Property() = default;
Property(Owner * const owr, Getter getmethod, Setter setmethod) : owner(owr), methodGet(getmethod), methodSet(setmethod) {}
void init(Owner * const owr, Getter getmethod, Setter setmethod)
{
owner = owr;
methodGet = getmethod;
methodSet = setmethod;
}
operator Type() { return (owner->*methodGet)(); } // Getter
void operator=(const Type & data) { (owner->*methodSet)(data); } // Setter
};
template<typename Owner, typename Type>
class Property<typename Owner, typename Type, ReadOnly_tag>
{
using Getter = Type(Owner::*)() const;
private:
Owner* owner;
Getter methodGet;
public:
Property() = default;
Property(Owner * const owr, Getter getmethod) : owner(owr), methodGet(getmethod) {}
void init(Owner* const owr, Getter getmethod)
{
owner = owr;
methodGet = getmethod;
}
operator Type() { return (owner->*methodGet)(); } // Getter
};
template<typename Owner, typename Type>
class Property<typename Owner, typename Type, WriteOnly_tag>
{
using Setter = void (Owner::*)(const Type&);
private:
Owner* owner;
Setter methodSet;
public:
Property() = default;
Property(Owner* const owr, Setter setmethod) : owner(owr), methodSet(setmethod) {}
void init(Owner* const owr, Setter setmethod)
{
owner = owr;
methodSet = setmethod;
}
void operator=(const Type& data) { (owner->*methodSet)(data); } // Setter
};
class Object
{
public:
Object(double vRW = 0., double vRO = 0., double vWO = 0.)
: valueRW(vRW), valueRO(vRO), valueWO(vWO)
{
ValueRW.init(this, &Object::getValueRW, &Object::setValueRW);
ValueRO.init(this, &Object::getValueRO);
ValueWO.init(this, &Object::setValueWO);
}
private:
double valueRW;
public:
Property<Object, double> ValueRW;
double getValueRW() const { return valueRW; }
void setValueRW(const double& v) { valueRW = v; }
private:
double valueRO;
public:
Property<Object, double, ReadOnly_tag> ValueRO;
double getValueRO() const { return valueRO; }
private:
double valueWO;
public:
Property<Object, double, WriteOnly_tag> ValueWO;
void setValueWO(const double& v) { valueWO = v; }
};
void main()
{
Object obj(5., 15., 25.);
obj.ValueRW = 10.;
cout << "value = " << obj.ValueRW << endl;
// obj.ValueRO = 10.; // Error! (ReadOnly)
cout << "value = " << obj.ValueRO << endl;
obj.ValueWO = 10.;
// cout << "value = " << obj.ValueWO << endl; // Error! (WriteOnly)
}
# include <iostream>
# include <memory>
using namespace std;
template<typename Implementation>
class Product
{
public:
virtual ~Product() { cout << "Destructor Product;" << endl; }
void run() { impl()->method(); }
private:
Implementation* impl()
{
return static_cast<Implementation*>(this);
}
void method() { cout << "Method Product;" << endl; }
};
class ConProd1 : public Product<ConProd1>
{
public:
virtual ~ConProd1() override { cout << "Destructor Conprod1;" << endl; }
private:
friend class Product<ConProd1>;
void method() { cout << "Method ConProd1;" << endl; }
};
class ConProd2 : public Product<ConProd2>
{
public:
virtual ~ConProd2() override { cout << "Destructor Conprod2;" << endl; }
};
int main()
{
unique_ptr<Product<ConProd1>> prod1 = make_unique<ConProd1>();
prod1->run();
unique_ptr<Product<ConProd2>> prod2 = make_unique<ConProd2>();
prod2->run();
}
# include <iostream>
# include <memory>
using namespace std;
class Base {
public:
virtual ~Base() = default;
virtual unique_ptr<Base> clone() const = 0;
};
template <typename Derived>
class Cloner : public Base
{
public:
virtual unique_ptr<Base> clone() const override
{
return unique_ptr<Base>(new Derived(*static_cast<const Derived*>(this)));
}
};
class Derived : public Cloner<Derived>
{
public:
Derived() { cout << "Default constructor of the Deriver class;" << endl; }
Derived(const Derived& dr) { cout << "Copy constructor of the Deriver class;" << endl; }
~Derived() { cout << "Destructor of the Deriver class;" << endl; }
};
void main()
{
unique_ptr<Base> b0 = make_unique<Derived>();
unique_ptr<Base> b1 = b0->clone();
}
# include <iostream>
using namespace std;
struct A
{
void f() {}
};
struct B {};
template <typename Type>
class Detecting
{
using P = void (Type::*)();
template<typename U, P = &U::f>
using True = short;
using False = char;
private:
template<typename U>
static True<U> detect(U*);
static False detect(...);
public:
static const bool exists = (sizeof(False) != sizeof(detect(static_cast<Type*>(0))));
};
void main()
{
cout << boolalpha << Detecting<A>::exists << endl; // true
cout << boolalpha << Detecting<B>::exists << endl; // false
}
# include <iostream>
using namespace std;
const class nullPtr_t
{
public:
// Может быть приведен к любому типу нулевого указателя (не на член класса)
template<class T>
inline operator T* () const { return 0; }
// или любому типу нулевого указателя на член
template<class C, class T>
inline operator T C::* () const { return 0; }
private:
void operator &() const = delete; // мы не можем взять адрес nullptr
} nullPtr = {};
void main()
{
int* i = nullPtr;
if (i == nullPtr)
cout << "null ptr;" << endl;
}
# include <iostream>
# include <memory>
using namespace std;
class Any
{
template <typename Type>
friend Type any_Cast(const Any& he);
template <typename Type>
friend const Type* any_Cast(const Any* he);
private:
class BaseValue;
unique_ptr<BaseValue> object;
public:
Any() = default;
Any(const Any& other);
Any(Any&& other) noexcept;
template <typename Type>
Any(const Type& val);
Any& operator =(const Any& other);
Any& operator =(Any&& other) noexcept;
template <typename Type>
Any& operator =(const Type& val);
template <typename Type>
Any& emplace(const Type& val);
bool has_value() const noexcept { return static_cast<bool>(object); }
const std::type_info& type() const noexcept
{
return object ? object->type() : typeid(void);
}
void reset() { object.reset(); }
template <typename Type>
operator Type() const;
private:
class BaseValue
{
public:
virtual ~BaseValue() = default;
virtual const std::type_info& type() const noexcept = 0;
virtual unique_ptr<BaseValue> clone() const = 0;
};
template <typename Type>
class Value : public BaseValue
{
private:
Type object;
public:
Value(const Type& t) : object(t) {}
virtual const std::type_info& type() const noexcept override
{
return typeid(object);
}
virtual unique_ptr<BaseValue> clone() const override
{
return make_unique<Value<Type>>(object);
}
Type get() const { return object; }
const Type* getPtr() const { return &object; }
};
};
template <typename Type>
Type any_Cast(const Any& he)
{
if (typeid(Type) != he.type())
{
throw std::runtime_error("Bad any_cast!");
}
return Type(he);
}
template <typename Type>
const Type* any_Cast(const Any* he)
{
Any::Value<Type>* type = dynamic_cast<Any::Value<Type>*>(he->object.get());
return type ? type->getPtr() : nullptr;
}
# pragma region Any methods
Any::Any(const Any& other)
{
if (other.object)
{
this->object = other.object->clone();
}
}
Any::Any(Any&& other) noexcept
{
this->object = move(other.object);
}
template <typename Type>
Any::Any(const Type& val)
{
this->object = make_unique<Value<Type>>(val);
}
Any& Any::operator =(const Any& other)
{
if (other.object)
{
this->object = other.object->clone();
}
else
{
this->object.reset();
}
return *this;
}
Any& Any::operator =(Any&& other) noexcept
{
this->object = move(other.object);
return *this;
}
template <typename Type>
Any& Any::operator =(const Type& val)
{
return emplace(val);
}
template <typename Type>
Any& Any::emplace(const Type& val)
{
this->object = make_unique<Value<Type>>(val);
return *this;
}
template <typename Type>
Any::operator Type() const
{
Value<Type>& type = dynamic_cast<Value<Type>&>(*object);
return type.get();
}
#pragma endregion
Any f()
{
Any temp = 7.5;
return temp;
}
void main()
{
try
{
Any v1 = 2, v2 = v1, v3 = f(), v4;
if (v3.has_value())
{
cout << v3.type().name() << endl;
if (v3.type() == typeid(double))
cout << "v3 = " << double(v3) << endl;
}
v4 = f();
v1.reset();
int j = 7;
int& aj = j;
v1 = j;
cout << "v1 = " << any_Cast<int>(v1) << endl;
cout << "v2 = " << any_Cast<int>(v2) << endl;
v2.emplace(5.0f);
cout << "v2 = " << any_Cast<float>(v2) << endl;
int i = v1;
float d = v2;
cout << "i = " << i << " f = " << d << endl;
}
catch (const std::exception& err)
{
cout << err.what() << endl;
}
}
# include <iostream>
# include <memory>
using namespace std;
template <typename TypeUnused>
class Function;
template <typename TypeReturn, typename ...Args>
class Function<TypeReturn(Args ...)>
{
class Function_holder_base;
using invoker_t = unique_ptr<Function_holder_base>;
private:
invoker_t mInvoker;
public:
Function() = default;
Function(const Function& other) : mInvoker(other.mInvoker->clone()) {}
template <typename TFunction>
Function(TFunction func)
: mInvoker(make_unique<Function_holder<TFunction>>(func)) {}
template <typename TypeFunction, typename TypeClass>
Function(TypeFunction TypeClass::* method)
: mInvoker(make_unique<Method_holder<TypeFunction, Args ...>>(method)) {}
Function& operator = (const Function& other)
{
mInvoker = other.mInvoker->clone();
return *this;
}
TypeReturn operator ()(Args ...args) { return mInvoker->invoke(args ...); }
private:
class Function_holder_base
{
public:
virtual ~Function_holder_base() = default;
virtual TypeReturn invoke(Args ...args) = 0;
virtual invoker_t clone() const = 0;
};
template <typename TFunction>
class Function_holder : public Function_holder_base
{
using self_t = Function_holder<TFunction>;
private:
TFunction mFunction;
public:
Function_holder(TFunction func) : mFunction(func) {}
virtual TypeReturn invoke(Args ... args) override { return mFunction(args ...); }
virtual invoker_t clone() const override
{
return invoker_t(make_unique<self_t>(mFunction));
}
};
template <typename TypeFunction, typename TypeClass, typename ...RestArgs>
class Method_holder : public Function_holder_base
{
using TMethod = TypeFunction TypeClass::*;
private:
TMethod mFunction;
public:
Method_holder(TMethod method) : mFunction(method) {}
virtual TypeReturn invoke(TypeClass obj, RestArgs ...restArgs) override
{
return (obj.*mFunction)(restArgs...);
}
virtual invoker_t clone() const override
{
return invoker_t(new Method_holder(mFunction));
}
};
};
struct Foo1
{
double smth(int x) { return x / 2.; }
};
struct Foo2
{
double smth(int x) { return x / 3.; }
};
class Test
{
int elem = 5;
public:
template <typename Tobj>
double result(Tobj& obj, Function<double(Tobj, int)> func)
{
return func(obj, this->elem);
}
};
void main()
{
Function<double(Foo1, int)> f1 = &Foo1::smth, f2;
Foo1 foo;
f2 = f1;
cout << "calling member function: " << f2(foo, 5) << endl;
Test ts;
cout << "calling member function: " << ts.result(foo, f2) << endl;
}
# include <iostream>
# include <exception>
using namespace std;
class bad_variant_access : public exception
{
public:
bad_variant_access() : exception("Bad variant access!") {}
};
template <typename ...Types>
class Variant
{
private:
template <typename ...Ts>
union UnionStorage {};
template <typename Head>
union UnionStorage<Head>
{
private:
Head head;
public:
UnionStorage() {}
~UnionStorage() {}
void destroy(int index)
{
if (index != 0) throw bad_variant_access();
head.Head::~Head();
}
template <typename Type>
int put(const Type& value, size_t index)
{
if (!std::is_same_v<Head, Type>) throw bad_variant_access();
new(&head) Type(value);
return index;
}
template <typename Type>
Type get(int index) const
{
if (index != 0 || !std::is_same_v<Head, Type>) throw bad_variant_access();
return *reinterpret_cast<const Type*>(&head);
}
int copy(const UnionStorage<Head>& stg, size_t index)
{
if (index != 0) throw bad_variant_access();
new(&head) Head(stg.head);
return index;
}
};
template <typename Head, typename ...Tail>
union UnionStorage<Head, Tail...>
{
private:
Head head;
UnionStorage<Tail...> tail;
public:
UnionStorage() {}
~UnionStorage() {}
void destroy(int index)
{
if (index == 0)
head.Head::~Head();
else
tail.destroy(index - 1);
}
template <typename Type>
int put(const Type& value, size_t index = 0)
{
if (!std::is_same_v<Head, Type>)
return tail.put(value, index + 1);
new(&head) Type(value);
return index;
}
template <typename Type>
Type get(int index) const
{
if (index == 0 && is_same_v<Head, Type>)
return *reinterpret_cast<const Type*>(&head);
return tail.get<Type>(index - 1);
}
int copy(const UnionStorage<Head, Tail...>& stg, size_t index)
{
if (index != 0)
return tail.copy(stg.tail, index - 1);
new(&head) Head(stg.head);
return index;
}
};
public:
Variant() = default;
Variant(Variant<Types...>& const vr);
Variant(Variant<Types...>&& vr) noexcept;
template <typename Type>
explicit Variant(Type&& value) { which = storage.put(value); }
~Variant() { destroy(); }
Variant& operator =(Variant<Types...>& const vr);
Variant& operator =(Variant<Types...>&& vr) noexcept;
template <typename Type>
Variant& operator =(Type&& value);
int index() const noexcept { return which; }
bool valueless_by_exception() const noexcept { return which == -1; }
template <typename Type>
Type get() const { return storage.get<Type>(which); }
private:
int which{ -1 };
UnionStorage<Types...> storage;
void destroy()
{
if (which != -1)
storage.destroy(which);
}
};
#pragma region Variant methods
template<typename ...Types>
Variant<Types...>::Variant(Variant<Types...>& const vr)
{
which = vr.which;
storage.copy(vr.storage, vr.which);
}
template<typename ...Types>
Variant<Types...>::Variant(Variant&& vr) noexcept
{
which = vr.which;
storage = vr.storege;
vr.which = -1;
}
template <typename ...Types>
Variant<Types...>& Variant<Types...>::operator =(Variant<Types...>& const vr)
{
destroy();
which = vr.which;
storage.copy(vr.storage, vr.which);
return *this;
}
template <typename ...Types>
Variant<Types...>& Variant<Types...>::operator =(Variant&& vr) noexcept
{
destroy();
which = vr.which;
storage = vr.storege;
vr.which = -1;
return *this;
}
template <typename ...Types>
template <typename Type>
Variant<Types...>& Variant<Types...>::operator =(Type&& value)
{
destroy();
which = storage.put(value);
return *this;
}
#pragma endregion
class Object
{
private:
int num = 10;
public:
Object() { cout << "Calling the default constructor!" << endl; }
Object(const Object& obj) { cout << "Calling the copy constructor!" << endl; }
~Object() { cout << "Calling the destructor!" << endl; }
int getNum() { return num; }
};
void main()
{
try
{
Variant<double, Object, int> var(5);
cout << var.get<int>() << endl;
var = 7.1;
cout << var.get<double>() << endl;
Object obj;
var = obj;
cout << var.get<Object>().getNum() << endl;
Variant<double, Object, int> var2(var);
var2 = var;
}
catch (bad_variant_access& err)
{
cout << err.what() << endl;
}
}