gpt4 book ai didi

c++ - 使用回调函数从构造函数调用虚拟/派生方法的替代方法?

转载 作者:行者123 更新时间:2023-11-30 03:21:38 26 4
gpt4 key购买 nike

我遇到了上述问题的情况。我想构建一棵具有关联节点的树。由于所有树的行为都相同但类型不同,因此本着继承的精神,我希望能够使用基类定义的相同树构造例程构造具有不同类型的树。我想知道适合我的情况的最佳做法是什么。

struct Tree
{
struct Node { std::list<std::shared_ptr<Node>> children_; };

Tree()
{
root_ = CreateNode();

// carry on adding nodes to their parents...
}

virtual std::shared_ptr<Node> CreateNode() { return std::shared_ptr<Node>(new Node()); }

std::shared_ptr<Node> root_;
};

struct TreeDerived : public Tree
{
struct NodeDerived : public Tree::Node {};

TreeDerived() : Tree() {}

virtual std::shared_ptr<Node> CreateNode() { return std::shared_ptr<NodeDerived>(new NodeDerived()); }
};

问题是,在构造基(显然)之前我无法调用派生函数,并且它使用 CreateNode 方法的基实现,该方法始终使用基节点实现构造树。这意味着我可以在不延迟树木数量的情况下 build 树木。显而易见的解决方案是对树进行模板化以采用不同的节点类型,使用特征强制节点类型?但是,这也意味着所有方法的定义都必须在标题中吗?这门课内容很丰富,所以我想尽可能避免这种情况,所以我考虑传递一个 lambda 来为我做这件事。

struct Tree
{
struct Node { std::list<std::shared_ptr<Node>> children_; };

Tree(std::function<std::shared_ptr<Node>()> customNodeConstructor)
{
root_ = customNodeConstructor();

// carry on adding nodes to their parents... using the customNodeConstructor to create the nodes.
}

std::shared_ptr<Node> root_;
};

struct TreeDerived : public Tree
{
struct NodeDerived : public Tree::Node {};

TreeDerived(std::function<std::shared_ptr<Node>()> customNodeConstructor) : Tree(customNodeConstructor) {}
};

这允许我派生并传递与派生树相关的 customNodeConstructor。由于返回的 shared_ptr 对象必须从 Tree::Node 派生,因此部分强制执行了类型安全,尽管不强制执行派生节点类型。

TreeDerived 实例化,它可能应该使用 TreeDerived::NodeDerived 只强制使用 Tree::Node 或派生类型,但不一定 TreeDerived::NodeDerived

然后可以像这样使用...

Tree tree([]() { return std::shared_ptr<Tree::Node>(); });

TreeDerived treeDerived([]() { return std::shared_ptr<TreeDerived::NodeDerived>(); });

这是好的做法还是我应该在不对 Tree 对象进行模板化的情况下做更多/其他事情?

非常感谢。

最佳答案

这个想法是合理的;但是,在派生类内部创建“自定义树构造函数”比在外部创建“自定义树构造函数”会更安全。这样,您就不会得到不正确的节点类型。代码形式:

struct TreeDerived : public Tree
{
struct NodeDerived : public Tree::Node {};

TreeDerived() : Tree([]() { return std::make_shared<NodeDerived>(); }) {}
};

另请注意,在一般情况下,std::function每次调用都会产生不小的运行时开销。如果你总是要传递一个无状态的 lambda,考虑在 Tree 中使用一个普通的函数指针。构造函数代替:

Tree(std::shared_ptr<Node> (*customNodeConstructor)())
{
root_ = customNodeConstructor();

// carry on adding nodes to their parents... using the customNodeConstructor to create the nodes.
}

作为这种“传入自定义创建者”方法的替代方法,您也可以只转换 Tree 的构造函数到函数模板中。它可能看起来像这样:

template <class T> struct TypeTag;

struct Tree
{
struct Node { std::list<std::shared_ptr<Node>> children_; };

template <class ConcreteNode>
Tree(TypeTag<ConcreteNode>)
{
root_ = std::make_shared<ConcreteNode>();

// carry on adding nodes to their parents...
}

std::shared_ptr<Node> root_;
};

struct TreeDerived : public Tree
{
struct NodeDerived : public Tree::Node {};

TreeDerived() : Tree(TypeTag<NodeDerived>{}) {}
};

然后构造函数模板必须定义在所有派生自 Tree 的类的某个地方。可以看到它的定义(很可能在头文件中),但是 Tree 的其余部分类保持正常。

关于c++ - 使用回调函数从构造函数调用虚拟/派生方法的替代方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52003554/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com