You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

209 lines
6.5 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <iostream>
#include <vector>
#include <memory>
#include <mutex>
#include <algorithm>
#include <stdexcept>
// Forward declarations
class INode;
class ILink;
class ILinkMixin;
using NodePtr = std::shared_ptr<INode>;
using LinkPtr = std::shared_ptr<ILink>;
// --- ИНТЕРФЕЙСЫ ---
class ILinkMixin {
public:
virtual ~ILinkMixin() = default;
virtual void linkChild(const NodePtr& child) = 0;
virtual void unlinkParent() = 0;
virtual LinkPtr getLink() = 0;
};
class INode : public ILinkMixin {
public:
~INode() override = default;
virtual const std::string& name() const = 0;
virtual NodePtr getSelf() = 0;
};
class ILink {
public:
virtual ~ILink() = default;
virtual NodePtr getNode() const = 0;
virtual LinkPtr getParent() const = 0;
virtual void setParent(const LinkPtr& parent) = 0;
virtual const std::vector<LinkPtr>& getChildren() const = 0;
virtual void addChild(const LinkPtr& child) = 0;
virtual void removeChild(const LinkPtr& child) = 0;
};
// --- РЕАЛИЗАЦИИ LINK ---
class BaseLink : public ILink {
public:
explicit BaseLink(NodePtr node) : owner_node_(node) {}
NodePtr getNode() const override { return owner_node_.lock(); }
LinkPtr getParent() const override { return parent_.lock(); }
void setParent(const LinkPtr& parent) override { parent_ = parent; }
const std::vector<LinkPtr>& getChildren() const override { return children_; }
void removeChild(const LinkPtr& child) override {
children_.erase(std::remove(children_.begin(), children_.end(), child), children_.end());
}
protected:
std::vector<LinkPtr> children_;
std::weak_ptr<INode> owner_node_;
std::weak_ptr<ILink> parent_;
};
class LeafLink : public BaseLink {
public:
using BaseLink::BaseLink;
void addChild(const LinkPtr&) override { throw std::logic_error("LeafLink cannot have children"); }
};
class OneToManyLink : public BaseLink {
public:
using BaseLink::BaseLink;
void addChild(const LinkPtr& child) override { children_.push_back(child); }
};
// --- CRTP МИКСИН-РЕАЛИЗАЦИЯ ---
template <class TFinalNode>
class LazyLinkMixin : public INode {
public:
void linkChild(const NodePtr& childNode) override {
lazyInit();
upgradeLinkIfNeeded();
LinkPtr childLink = childNode->getLink();
childLink->setParent(link_);
link_->addChild(childLink);
}
void unlinkParent() override {
lazyInit();
LinkPtr parentLink = link_->getParent();
if (!parentLink) return;
parentLink->removeChild(this->getLink());
link_->setParent(nullptr);
// downgradeLinkIfPossible() здесь не нужен, т.к. узел просто отсоединяется
}
LinkPtr getLink() override {
lazyInit();
return link_;
}
protected:
virtual std::shared_ptr<TFinalNode> getShared() = 0;
NodePtr getSelf() override { return getShared(); }
private:
void lazyInit() {
if (!link_) {
link_ = std::make_shared<LeafLink>(getSelf());
}
}
// ИСПРАВЛЕНО: Эта функция теперь корректно обновляет родителя
void upgradeLinkIfNeeded() {
if (!std::dynamic_pointer_cast<LeafLink>(link_)) return;
// Создаем новую, улучшенную связь
auto newLink = std::make_shared<OneToManyLink>(getSelf());
// Если был родитель, уведомляем его о замене
if (auto parentLink = link_->getParent()) {
parentLink->removeChild(link_); // Удаляем старую связь
parentLink->addChild(newLink); // Добавляем новую
}
// Переносим родителя в новую связь и заменяем старую
newLink->setParent(link_->getParent());
link_ = newLink;
}
// ИСПРАВЛЕНО: Эта функция теперь тоже корректно обновляет родителя
void downgradeLinkIfPossible() {
if (link_->getChildren().empty() && std::dynamic_pointer_cast<OneToManyLink>(link_)) {
// Создаем новую, упрощенную связь
auto newLink = std::make_shared<LeafLink>(getSelf());
// Если был родитель, уведомляем его о замене
if (auto parentLink = link_->getParent()) {
parentLink->removeChild(link_); // Удаляем старую связь
parentLink->addChild(newLink); // Добавляем новую
}
// Переносим родителя в новую связь и заменяем старую
newLink->setParent(link_->getParent());
link_ = newLink;
}
}
LinkPtr link_;
};
// --- КОНКРЕТНЫЙ УЗЕЛ ---
class SimpleNode : public LazyLinkMixin<SimpleNode>,
public std::enable_shared_from_this<SimpleNode> {
public:
static NodePtr create(std::string name) {
struct EnableMakeShared : public SimpleNode {
EnableMakeShared(std::string n) : SimpleNode(std::move(n)) {}
};
return std::make_shared<EnableMakeShared>(std::move(name));
}
const std::string& name() const override { return name_; }
protected:
std::shared_ptr<SimpleNode> getShared() override {
return shared_from_this();
}
private:
friend class LazyLinkMixin<SimpleNode>;
explicit SimpleNode(std::string name) : name_(std::move(name)) {}
std::string name_;
};
// --- УТИЛИТА ДЛЯ ВЫВОДА ---
void printTree(const NodePtr& startNode, int indent = 0) {
if (!startNode) return;
for (int i = 0; i < indent; ++i) std::cout << " ";
std::cout << startNode->name() << "\n";
LinkPtr nodeLink = startNode->getLink();
for (const auto& childLink : nodeLink->getChildren()) {
printTree(childLink->getNode(), indent + 1);
}
}
// --- MAIN ---
int main() {
auto root = SimpleNode::create("Root");
auto child1 = SimpleNode::create("Child1");
auto child2 = SimpleNode::create("Child2");
root->linkChild(child1);
root->linkChild(child2);
auto subchild = SimpleNode::create("SubChild");
child1->linkChild(subchild);
std::cout << "Initial tree:\n";
printTree(root);
std::cout << "\nUnlinking Child1...\n";
child1->unlinkParent();
std::cout << "\nFinal tree:\n";
printTree(root);
return 0;
}