C++ class 改变“*this”指的是什么
C++ class changing what "*this" refers to
我有一个 class "foo",其中包含一个成员向量,其中包含 "foo" 类型的元素。 class 有一个名为 "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" 对象,但老实说我不知道如何正确构建这样的程序,或者该程序的最佳实践版本如何 look/work。谁能告诉我 "right" 做事的方法,谁就能加分。
回到真正的问题:我如何制作这样的东西,特别是 "setAsNode" 方法,真正起作用?另外,为什么我的示例中的代码不起作用?请注意,它编译得很好,只是在 运行 上崩溃了。
在您的示例中,对 foo.get(0).get(0).setAsNode()
的调用将尝试将 foo.get(0).get(0)
的值复制到 foo
。在此过程中,foo.children
将被分配一个新值,导致 vector 清除其先前的元素,从而导致 foo.get(0).get(0)
被破坏。这意味着 this
已经被销毁,指针无法使用。但是,这是在我们当前使用 this
的赋值操作期间发生的。要解决这个问题,您必须确保被复制的值持续存在足够长的时间才能被复制。直观的解决方案可能是在分配之前复制要分配的值。
foo& setAsNode() {
auto this_copy = *this;
*originalNode = std::move(this_copy);
return *originalNode;
}
这会起作用,你仍然要小心,不要在赋值给 *originalNode
之后使用 this
。另一种解决方案是在执行分配之前控制 originalNode
的子向量。在此版本中,this
在方法 returns 之前一直有效,但如果以下赋值抛出异常,您的树将处于无效状态。
foo& setAsNode() {
auto original_vect = std::move(originalNode->children);
*originalNode = *this;
return *originalNode;
}
总而言之,我会对要求对象自杀的设计持谨慎态度。这意味着对象控制自己的所有权,或者所有权责任是周期性的。
C++ 方法(可以说是唯一正确的方法)是分离关注点。
foo 不是(或不应该是)foo-finder。它应该做 foo-things,而不是 foo-navigation 的事情。
创建一个新的 class 作为 foo 的游标或迭代器。
这是一个稍微扩展的版本,其中 foo_cursor 记住了它在 foo 堆栈中的旅程。可能对你的问题有点矫枉过正,但它展示了将 foo-navigation 逻辑与 foo-implementation 逻辑分开的原则。
你做的越多,你的程序就越容易编写、调试和维护。
#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
我有一个 class "foo",其中包含一个成员向量,其中包含 "foo" 类型的元素。 class 有一个名为 "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" 对象,但老实说我不知道如何正确构建这样的程序,或者该程序的最佳实践版本如何 look/work。谁能告诉我 "right" 做事的方法,谁就能加分。
回到真正的问题:我如何制作这样的东西,特别是 "setAsNode" 方法,真正起作用?另外,为什么我的示例中的代码不起作用?请注意,它编译得很好,只是在 运行 上崩溃了。
在您的示例中,对 foo.get(0).get(0).setAsNode()
的调用将尝试将 foo.get(0).get(0)
的值复制到 foo
。在此过程中,foo.children
将被分配一个新值,导致 vector 清除其先前的元素,从而导致 foo.get(0).get(0)
被破坏。这意味着 this
已经被销毁,指针无法使用。但是,这是在我们当前使用 this
的赋值操作期间发生的。要解决这个问题,您必须确保被复制的值持续存在足够长的时间才能被复制。直观的解决方案可能是在分配之前复制要分配的值。
foo& setAsNode() {
auto this_copy = *this;
*originalNode = std::move(this_copy);
return *originalNode;
}
这会起作用,你仍然要小心,不要在赋值给 *originalNode
之后使用 this
。另一种解决方案是在执行分配之前控制 originalNode
的子向量。在此版本中,this
在方法 returns 之前一直有效,但如果以下赋值抛出异常,您的树将处于无效状态。
foo& setAsNode() {
auto original_vect = std::move(originalNode->children);
*originalNode = *this;
return *originalNode;
}
总而言之,我会对要求对象自杀的设计持谨慎态度。这意味着对象控制自己的所有权,或者所有权责任是周期性的。
C++ 方法(可以说是唯一正确的方法)是分离关注点。
foo 不是(或不应该是)foo-finder。它应该做 foo-things,而不是 foo-navigation 的事情。
创建一个新的 class 作为 foo 的游标或迭代器。
这是一个稍微扩展的版本,其中 foo_cursor 记住了它在 foo 堆栈中的旅程。可能对你的问题有点矫枉过正,但它展示了将 foo-navigation 逻辑与 foo-implementation 逻辑分开的原则。
你做的越多,你的程序就越容易编写、调试和维护。
#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