C ++类更改“* this”指的是

时间:2017-06-02 13:55:22

标签: c++ class vector this variable-assignment

我有一个类“foo”,它包含一个包含“foo”类型元素的成员向量。这个类有一个名为“make”的方法,它创建“foo”对象并将它们附加到向量。我还提供了一种导航“foo”对象的方法,以及名为“get”的“foo”向量元素。

我想重新分配“this”在任何“foo”对象上引用的内容,以指向其向量中的foo对象。我想给代码提供这种功能,以便更有效地导航和跟踪foo对象,而无需将引用绑定到特定的“foo”成员。我试图通过一个名为“setAsNode”的方法来实现这一点,该方法重新分配了“* this”。

这是我嘲笑的一些示例代码,我相信我的观点是:

struct foo{
    foo(const std::string &r):s(r){}
    foo& make(const std::string &r){children.push_back(test(r)); children.back().originalNode = originalNode; return children.back();}
    foo& setAsNode(){*originalNode = *this; return *originalNode;}
    foo& get(int i){return children.at(i);}
private:
    std::vector<foo> children;
    std::string s = "someData";
    foo *originalNode = this;
};

以及如何发挥作用的实际例子:

foo f1("test1");
f1.make("test2").make("test3");//pushes a new foo element back to the vector in f1 and pushes a new foo element back to it's vector
f1.get(0).get(0);//refers to the deepest nested object we just made above, cumbersome to type especially if the nesting is deep
f1.get(0).get(0).setAsNode();//this is where the error occurs, the desired effect is to make f1 the same object as denoted by f1.get(0).get(0);

foo& f2 = f1.get(0).get(0);//f2 would be equivalent to what I want the above code to reset f1 to be (or at least how I'd like it to act)

我意识到我可能正在实现一些非常糟糕的编程实践,例如使用引用而不是指针来返回“foo”对象,但老实说我不知道​​如何正确构建这样的程序,或者如何最佳实践这个程序的版本看起来/工作。任何可以向我展示“正确”做事方式的人的奖励积分。

回到真正的问题:如何制作这样的东西,特别是“setAsNode”方法,实际上有效吗?另外,为什么我的示例中的代码不起作用?注意,它编译很好,只是在运行时崩溃。

2 个答案:

答案 0 :(得分:2)

c ++方式(可以说是唯一正确的方法)是分开关注

foo不是(或不应该)foo-finder。它应该做foo-things,而不是foo-navigation的东西。

创建一个新类作为foo的游标或迭代器。

这是一个稍微扩展的版本,其中foo_cursor记住它通过foo堆栈的旅程。你的问题可能有些过分,但它证明了将foo导航逻辑与foo实现逻辑分离的原则。

执行此操作越多,程序编写,调试和维护就越容易。

#include <utility>
#include <string>
#include <vector>
#include <stack>
#include <stdexcept>
#include <iostream>


struct foo{
    foo(const std::string &r)
        : children()
        , s(r)
    {}

    foo& make(const std::string &r)
    {
        children.emplace_back(r);
        return children.back();
    }

    foo& get(int i)
    {
        return children.at(i);
    }

    void print() const {
        std::cout << s << std::endl;
    }


private:

    std::vector<foo> children;
    std::string s = "someData";
};

struct foo_cursor
{
    foo_cursor(foo& f)
        : current_(std::addressof(f))
    {}

    foo_cursor& down(int i)
    {
        history_.push(current_);
        current_ = std::addressof(current_->get(i));
        return *this;
    }

    foo_cursor& up() {
        if (history_.empty()) {
            throw std::logic_error("went up too far");
        }
        else {
            current_ = history_.top();
            history_.pop();
        }
        return *this;
    }

    foo* operator->() const {
        return current_;
    }

private:
    foo* current_;
    std::stack<foo*> history_;
};


int main()
{
    foo f("a");
    f.make("b").make("c");

    auto fc = foo_cursor(f);
    fc.down(0).down(0)->print();
    fc.up()->print();
    fc.up()->print();
}

预期产出:

c
b
a

答案 1 :(得分:0)

在您的示例中,对foo.get(0).get(0).setAsNode()的调用将尝试将foo.get(0).get(0)的值复制到foo。在此过程中,foo.children将被赋予一个新值,导致向量清除它之前导致foo.get(0).get(0)被破坏的元素。这意味着this已被破坏,并且指针无法使用。但是,这是在我们当前使用this的分配操作期间发生的。要解决此问题,您必须确保复制的值持续时间足以复制。直观的解决方案可能是在分配之前将值复制到assign。

foo& setAsNode() { 
    auto this_copy = *this;
    *originalNode = std::move(this_copy); 
    return *originalNode; 
}

这样可行,您必须小心,在分配到this后永远不要使用*originalNode。另一种解决方案是在执行赋值之前控制originalNode的子向量。在此版本中,this在方法返回之前保持有效,但如果以下分配引发异常,则树将保持无效状态。

foo& setAsNode() { 
    auto original_vect = std::move(originalNode->children);
    *originalNode = *this;
    return *originalNode; 
}

总而言之,我会警惕一种要求物体自杀的设计。它意味着对象控制自己的所有权,或者所有权责任在其他方面具有周期性。