【问题标题】:C++ return by value - what happens with the pointer inside?C++ 按值返回 - 里面的指针会发生什么?
【发布时间】:2015-05-25 04:34:36
【问题描述】:

所以我有一点 SegFaults 并且想知道是否有人可以更深入地解释我这是如何工作的。

我有一个围绕数字变量的包装类,我正在尝试构建表达式的计算树。这是一个示例代码:

class DoubleWrap{ 
public:
   static int id;
   DoubleWrap(double value){this->value = value;myid=++id;}
   double value;
   int myid;
   std::vector<DoubleWrap*> parents;
   DoubleWrap operator+(DoubleWrap& x){
      DoubleWrap result(this->value + x.value);
      setParents(*this,result);
      setParents(x,result);
      return result;
   }
   void setParents(DoubleWrap& parent,DoubleWrap& child){
      child.parents.push_back(&parent);
   }
   void printTree(){
      std::cout<< "[" << this->myid << "]-(";
      for (auto& elem: this->parents)
          std::cout<< elem->myid << "," << elem <<",";
      std::cout<<")"<<std::endl;
      for (auto& elem: this->parents)
          elem->printTree();
  }
}

我遇到的问题是像 x = a+b+c+d; 这样的表达式。当我尝试寻找 x 的父母时,它有 2 个父母,但 parents[0].parents[0] 是 nil,例如由于某种原因,指向分配位置的指针不起作用或分配的内存被破坏?

如果有人可以就解决方法以及为什么会发生这种情况向我提供建议,我非常感兴趣。

举一个关于这段代码的例子:

DoubleWrap dw(12.6);
DoubleWrap dw2(12.5);
DoubleWrap dw3(12.4);
DoubleWrap dw4(12.3);
DoubleWrap a = dw + dw2 + dw3;
DoubleWrap b = a + dw+ dw2+ dw3 + dw4;
b.printTree();

我期望输出:

[10]-(9,0x13a4b50,4,0x7fff5bdb62f0,)
[9]-(8,0x13a4b00,3,0x7fff5bdb62c0,)
[8]-(7,0x13a49f0,2,0x7fff5bdb6290,)
[7]-(6,0x7fff5bdb6320,1,0x7fff5bdb6260,)
[6]-(5,0x13a4db0,3,0x7fff5bdb62c0,)
[5]-(1,0x7fff5bdb6260,2,0x7fff5bdb6290,)
[1]-()
[2]-()
[3]-()
[1]-()
[2]-()
[3]-()
[4]-()

但我得到的结果(通常在不同的运行中不同):

[10]-(9,0x7ffffd5e2f00,4,0x7ffffd5e2de0,)
[9]-(33,0x1c6da10,3,0x7ffffd5e2db0,)
Process finished with exit code 139

我的猜测是运算符的返回实际上复制了 DoubleWrap 变量,因此父母中的指针指向的值超出范围并释放内存?

临时的解决方案是通过引用返回,但问题是为什么以及是否有合适的?

PS:修正了我之前遇到的一个错误,它是 setParents 中的一个错误。

【问题讨论】:

  • 只是不相关,但你用“一点 SegFaults”让我开心:))
  • operator+ 修改参数很奇怪。
  • 你是如何编译的?
  • @NeilKirk 好点子,应该(可能)通过const 参考,以确保operator+= 表现良好(即,应该做与POD 一样的事情,例如int)
  • 该代码无法编译,您的向量需要指向对象的指针,而您正在将对象推回其中。

标签: c++ pointers memory memory-management operator-overloading


【解决方案1】:

您的代码: DoubleWrap 运算符+(DoubleWrap& x){ DoubleWrap 结果(this->value + x->value); 如果 x 是指向 DoubleWrap 类的指针,但您已将 x 编码为 DoubleWrap 类对象而不是指针,那么您的代码将是正确的 应该读 : DoubleWrap 运算符+(DoubleWrap& x){ DoubleWrap 结果(this->value + x.value);

【讨论】:

  • 这不是代码中唯一的错误。 setParents 缺少返回类型,他正在传递一个需要传递指针的对象,child 之后的 &amp; 应该在 child 之前,等等。这些只是语法错误。跨度>
【解决方案2】:

您的问题是您正在将指向 临时对象 的指针添加到向量中。

DoubleWrap a = dw + dw2 + dw3;

上面会先调用dw.operator+(dw2)。这会创建一个临时对象,我们将其命名为 temp。然后它调用temp.operator+(dw3)。现在在operator+ 内部有对setParents 的调用,您将temp 作为parent 传递。然后,您获取temp 的地址(至少为了编译代码应该这样做)并将其添加到child 的向量中。

现在当对operator+ 的调用返回时,temp 被销毁,a 中的向量包含一个指向不存在对象的指针。


您可以通过使用shared_ptr 将元素存储在向量中来解决此问题。请注意,如果引用循环是可能的,您应该小心使用weak_ptr 打破这些循环(我没有在下面的代码中包含实现)。下面是最终代码的样子:

class DoubleWrap {
public:
   static int id;
   DoubleWrap(double value) : value(value), myid(++id) {}
   double value;
   int myid;
   std::vector<std::shared_ptr<DoubleWrap>> parents;
   DoubleWrap operator+(const DoubleWrap& x) const {
      DoubleWrap result(this->value + x.value);
      setParents(*this, result);
      setParents(x, result);
      return result;
   }
   static void setParents(const DoubleWrap& parent, DoubleWrap& child) {
      child.parents.push_back(std::make_shared<DoubleWrap>(parent)); // makes a copy of parent
   }
   void printTree() const {
      std::cout << "[" << this->myid << "]-(";
      for (const auto& elem: this->parents)
         std::cout << elem->myid << "," << elem.get() << ",";
      std::cout << ")" << std::endl;
      for (const auto& elem: this->parents)
         elem->printTree();
   }
};

int DoubleWrap::id = 0;

【讨论】:

  • std::vector&lt;DoubleWrap&gt; 导致未定义的行为;标准容器不能有不完整的类型作为模板参数。 See here
  • 我不认为有一个简单的解决方案。它可以使用shared_ptr 的向量,但我担心可能会出现引用循环。 (这也限制了调用代码不使用自动分配给任何破坏目的的 DoubleWraps)。 reference_wrapper 只是包装了一个指针,所以同样的生命周期问题仍然存在
  • @MattMcNabb 如果DoubleWrap 是不完整的类型,为什么会编译?编译器肯定有这里的所有信息,毕竟它不是前向声明。
  • @Galik 请参阅我的第一条评论中的链接。或者here 是同一主题的另一个问题
  • 好的,但是当对象超出范围时如何保留它?一种可行的方法是从 operator+ 返回引用,但是很多人建议我不要这样做?是不是很糟糕。顺便说一句,向量是指向 DoubleWrap 的指针类型,这很糟糕吗?
猜你喜欢
  • 2015-07-10
  • 2018-01-08
  • 1970-01-01
  • 2014-11-15
  • 2021-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多