我有一个类“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”方法,实际上有效吗?另外,为什么我的示例中的代码不起作用?注意,它编译很好,只是在运行时崩溃。
答案 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;
}
总而言之,我会警惕一种要求物体自杀的设计。它意味着对象控制自己的所有权,或者所有权责任在其他方面具有周期性。