【问题标题】:what /can/ you return from a function .. and expect it to be alive at arrival?你/可以/你从一个函数返回什么......并期望它在到达时还活着?
【发布时间】:2020-01-17 14:04:57
【问题描述】:

我又一次因为期望一个函数返回一个正确的值而感到失望……相反,我得到了奇怪的行为和误导性的调试信息。 众所周知,您不能从函数返回局部变量并期望它按预期到达。测试

int i=2;
int k=4;
return make_pair<int,int>(i*i,k*k);

确实返回了一些可敬的东西。但是使用比简单类型更复杂的对象似乎每次都会吸引我。 那么,是否有任何形式可以用来区分什么可以从函数中安全返回,什么不能安全返回? ------------ 编辑时添加:------------ 这是一个行不通的例子,断章取义。 问题上下文是屏幕的矩形树(将是 GUI)。 类节点继承自包含 3 个指向普通类型的指针的基(矩形)(同样,用于使值保持不变).. 基在构造函数中使用 new

pair<node,node> node_handler::split( vector<node>::iterator& this_node, double ratio, bool as_horizontal ){
//this_node becomes parents to the split-twins
    this_node->my_ratio=ratio;
    double firstW, firstH;
    double secW, secH;
    glm::dvec2 afirst, asecond;
    if(as_horizontal ){
        firstW = *this_node->plWidth*LETTER_PIXEL_WIDTH;
        firstH = *this_node->plHeight*LINE_PIXEL_HEIGHT*ratio;
        afirst = *this_node->pPoint;
        secW   = firstW;
        secH   = LINE_PIXEL_HEIGHT*(*this_node->plHeight)*(1.0d-ratio);
        asecond= afirst+glm::dvec2(0.0d, firstH);
    }
    else{
        firstW = ratio*(*this_node->plWidth)*LETTER_PIXEL_WIDTH;
        firstH = *this_node->plHeight*LINE_PIXEL_HEIGHT;
        afirst = *this_node->pPoint;
        secW   = (1.0d*ratio)*(*this_node->plWidth)*LETTER_PIXEL_WIDTH;
        secH   = firstH;
        asecond= afirst+glm::dvec2(firstW,0.0d);
    }
    return make_pair<node,node>( node(afirst ,firstW, firstH) , node(asecond ,secW, secH)  ) ;
}

【问题讨论】:

  • 这是一个非常模糊的问题,但通常您返回 按值 的任何内容都可以。如果返回指针或引用,则该对象的生命周期必须比本地函数长。
  • 所以你展示了你说工作的代码,你说你没有展示的其他一些代码不起作用?显示不起作用的代码。
  • “你不能从一个函数返回一个局部变量并期望它如你所愿地到达”——这是完全错误的。
  • Pete Becker,我对它不起作用并不感到惊讶,但不是为什么(直到现在)。我关注了构造函数和析构函数,并注意到 pair 在构造后立即被破坏......并且在此之后执行将停止。
  • 如果上面的示例导致执行停止而没有指向失败的特定代码行,我是否应该假设节点的默认复制构造函数不足?

标签: c++ return


【解决方案1】:

从技术上讲,您可以从函数中返回任何内容。

现在,当您返回一个指针或对仅本地的东西的引用时,您就有问题了。

解决方案:

  1. 返回副本(无论如何都可以使用复制省略)
  2. 返回 shared_ptr&lt;&gt;/unique-ptr&lt;&gt; 以获取不得复制的内容。
  3. 仅返回基本类型并将对可能被修改的对象的引用传递给函数。

不要在函数中创建需要手动销毁层的东西(比如用new创建的指针)。

【讨论】:

  • 注意:C++ 版本越现代,我越不鼓励#3,通过可变引用传递虚拟“返回”,特别是如果参数也用作输入。从调用方来看,代码看起来与通过值或常量引用传递的“安全”代码相同,但如果参数不是 100% 明显是逻辑返回(例如 std::swap 之类的东西),人们可能会被就地突变,假设它纯粹是一个输入值。除此之外,这意味着您必须提前显式声明变量,并且无法从使用auto 推断返回类型中受益。
  • 我想知道我在传递的内容中哪里出错了。如果我想让值保持不变,我觉得不得不使用指针(hench the rectangle-pointers)。大多数工作将以 vector 或 list .. 结束,而我没有意识到 /that's/ 是唯一需要的分配器。我对手头示例的解毒剂是再次制作一对 。它解决了手头的问题,但可能不是最好的解决方案。我非常感谢您的所有意见和建议。
  • @ShadowRanger 在 Windows API 中,绝大多数函数都是这样工作的,所以用户必须应付。
  • @MichaelChourdakis:Windows API 在很大程度上与 C 兼容,因此核心 API 中甚至都没有引用。它在 API 的外围部分中使用的真正类似 C++ 的功能通常早于可靠/保证复制省略,并且通常使用状态代码的返回值而不是引发异常(再次,C 继承,并且要求所有使用异常的程序被认为是错误的形式),因此#3是唯一的选择。当然,用户必须处理现有的 API,但这并不意味着编写 新的 API 是一个好主意,这些 API 不必要地缓慢/令人困惑。
  • @ShadowRanger Windows API 在很大程度上是一个 C++ api。 C 的残余仅适用于较旧的 APIS。最新的 api、COM、Ribbon、Sensors、DirectX、Direct2D、Media Foundation、UWP。 [....] 是 C++,它仍然使用 #3 形式。此外,C++ 并不总是意味着复制省略,你不能期望 API 只与 C++ 17 兼容。
【解决方案2】:

我明白了,包含指针成员的类合理地必须具有自定义复制/赋值运算符。我从来没有掌握我当时阅读的书中提到的“rho”变量……“right_hand_object”一定是!这就是我的顿悟。正是在关注构造函数的业务以及您对可复制对象的讨论时,才解决了我的这个老 rho 问题。

很抱歉将我的挫败感传播给你。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-06
    • 2018-12-21
    • 1970-01-01
    • 2020-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-20
    相关资源
    最近更新 更多