【问题标题】:Create std::list of value instead of std::list of pointers in recursive function在递归函数中创建 std::list of value 而不是 std::list of pointers
【发布时间】:2012-07-05 08:24:32
【问题描述】:

我有这门课:

class obj
{
public:

    obj()
        : parent(nullptr),
        depth(0)
    {   }

    obj* parent;
    list<obj> children;
    int depth;  // Used only for this example
};

为了填充我的数据结构,我使用如下递归函数:

void recursive(obj& parent)
{
    if(parent.depth == 1)
        return;

    obj son;
    son.parent = &parent;
    son.depth = parent.depth + 1;

    recursive(son);

    parent.children.push_back(son);
}

以这种方式为例:

obj root;
recursive(root);

如果你注意你可以看到,如果递归函数中的测试是:

if(parent.depth == n)
    return;

对于n &gt;= 2,此代码将不起作用(一旦退出递归函数,“孙子”root-&gt;son-&gt;son 的父级的存储地址 - 等等 - 将不是有效地址)。

解决此问题的一种方法是使用指针列表 (list&lt;obj*&gt; children) 而不是值列表:

void recursive(obj& parent)
{
    if(parent.depth == 2)
        return;

    obj* son_ptr = new obj();
    son_ptr->parent = &parent;
    son_ptr->depth = parent.depth + 1;

    recursive(*son);

    parent.children.push_back(son_ptr);
}

是否有另一种方法可以完成相同的工作并将objs 存储在值列表中而不是指针列表中?

【问题讨论】:

  • 你可以使用工厂来创建对象吗?
  • 这是一个design pattern。有一个“工厂”对象,该对象创建 objs 并将每个对象的内部保存的唯一 ID 映射到其当前指针。每个obj 可以保留对工厂对象的引用,并在复制构造函数期间告诉工厂新地址并使旧地址无效。然后你可以保留一个父 ID,并在需要时向工厂索要它的指针。
  • 我不认为我可以使用,但我不确定,因为我以前从未使用过“工厂”......无论如何,谢谢!
  • 尼克:你不知道怎么做,有人提出了一些听起来很有希望的东西,说“我不认为我可以使用它[因为我以前没有做过]/谢谢无论如何”真的是不屑一顾、不欣赏和适得其反——如果你在你现在不会问之前解决了这个问题。你为什么不说“对不起,麻烦了,但我不知道如何实施你的建议,你能给我更多的帮助吗?”。
  • 目前的问题是不明确的。我们不知道你为什么要按照你所说的去做。我们不知道要求——什么可以改变,什么不能改变。如果你澄清了问题,我可以举一个简短的例子,但在那之前我不会猜测什么会起作用或不会起作用。

标签: c++ pointers recursion c++11


【解决方案1】:

在开始创建更多子对象之前,不只是固定对象的地址吗?为此,首先将它们放入子列表中,然后递归...

void recursive(obj& parent, int n)
{
    if (parent.depth == n)
        return;

    obj son;
    son.parent = &parent;
    son.depth = parent.depth + 1;
    parent.children.push_back(son);

    recursive(parent.children.back(), n);
}

【讨论】:

  • 是的,这是一个有效的答案 +1,但我现在无法更改指令的顺序。
  • @Nick:告诉我们你不能做这样的事情而不解释为什么很难提出解决方案。尽管如此,我还是会抛出另一个从您的角度来看可能会或可能不会重新排序的内容:您可以在recursive() 中省略设置son.parent,而是使用一个额外的函数参数来计算深度,在每个递归调用中添加1 ,然后有第二个函数返回到结构中并在 recursive() 展开后填充 .parent 指针,将值复制到它们的最终列表位置。
  • 请耐心等待...我正在尽力使用“有点”复杂的真实代码...如果我可以使用您的解决方案调整我的代码,我会标记作为答案。
【解决方案2】:

您可能会考虑在objs 中存储唯一ID,这样obj 的实例不会定义所表示对象的身份,而是定义其ID。如果这样做,您可能会存储 ID 以链接相关的 objs,而不是存储指针。

例如,您可以更改obj 类以包含int 字段id

class obj {
public:
    obj()
        : parent(nullptr),
          depth(0)
    {
        // Not thread-safe; would need to get protected if multi-threaded
        id = nextId++;
    }

    static int nextId;

    int id;
    int parentId;
    list<int> childrenIds;

    int depth;  // Used only for this example
};

每当您构造一个新的obj 来表示一个新的逻辑“事物”时,您都可以为id 字段分配一个新的唯一值。当您想在代表相关“事物”的objs 之间建立关系时,您可以——例如——使用parentId 字段而不是parent 指针字段:

void recursive(obj& parent)
{
    if (parent.depth == 1) {
        return;
    }

    obj son;
    son.parentId = parent.id;
    son.depth = parent.depth + 1;

    recursive(son);

    parent.childrenIds.push_back(son.id);
}

当您想要跟随链接时,这需要更多的工作:您不需要跟随指向父 obj 的指针,而是需要在您维护的一些全局 obj 列表中查找 obj(搜索对于有问题的parentId)。

当然,这实际上取决于这些objs 真正代表的内容,以及您试图实现的更广泛的目标......

【讨论】:

  • 对不起,我不明白你的意思,你能给我举个例子吗?
  • @Nick 我已经提供了更多细节,但它相当抽象。这有帮助吗?
  • 嗯......我不认为你的回答可以帮助我列出价值:(
  • @Nick 是什么让你这么认为?我的答案解决了对指向无效对象的指针损坏的担忧——按照我的建议,你不会有指针,因此没有什么可以破坏的。如果您认为,也许我解释得不够好......
  • 尝试用 C++ 代替英文回答,也许这会有所帮助。
猜你喜欢
  • 1970-01-01
  • 2012-04-07
  • 2019-10-30
  • 1970-01-01
  • 1970-01-01
  • 2019-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多