C++ return 按值 - 里面的指针会发生什么?

C++ return by value - what happens with the pointer inside?

所以我遇到了一些段错误,想知道是否有人可以更深入地向我解释这是如何工作的。

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

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 的 parents 时,它有 2 个 parents,但 parents[0].parents[0] 为零,例如由于某种原因,指向分配位置的指针不起作用或分配的内存被破坏?

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

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

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

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

临时解决方案是return参考,但问题是为什么以及是否有合适的解决方案?

PS: 修复了之前我得到的一个错误,是 setParents 的错误。

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

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

DoubleWrap a = dw + dw2 + dw3;

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

的向量中

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


您可以通过使用 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;