【问题标题】:C++ Cannot Return Object From FunctionC++ 无法从函数返回对象
【发布时间】:2014-05-15 12:30:57
【问题描述】:

我正在尝试使用 C++“剪辑器库”(http://www.angusj.com/delphi/clipper.php),但是当我尝试从函数中返回剪辑器库中的一个对象时,它似乎变为空或以某种方式被更改

这是我写的函数。唯一相关的行应该是最后 3 行。

ClipperLib::PolyTree MeshHandler::trianglesToPolyTreeUnion(std::vector<Triangle> triangles)
{
    // Make all of the triangles CW
    for (auto& triangle : triangles)
    {
        triangle.makeClockwise();
    }
    // Set up the Clipper
    ClipperLib::Clipper clipper;
    // To take a union, add all the paths as "subject" paths
    for (auto& triangle : triangles)
    {
        ClipperLib::Path triContour(3);
        triContour[0] = convertGLMToClipperPoint(triangle.getVertex(0));
        triContour[1] = convertGLMToClipperPoint(triangle.getVertex(1));
        triContour[2] = convertGLMToClipperPoint(triangle.getVertex(2));
        clipper.AddPath(triContour, ClipperLib::PolyType::ptSubject, true);
    }
    // Now get the PolyTree representing the contours
    ClipperLib::PolyTree tree;
    clipper.Execute(ClipperLib::ClipType::ctUnion, tree);
    return tree;
}

当我调用clipper.execute 时,它​​会将一些轮廓信息写入树结构。它写入了正确的信息,我已经测试它是正确的。但是,当我返回树时,它似乎没有复制任何内容,并且此函数产生的 PolyTree 是空的。

我确信这个库没有任何问题,而且我只是在这里犯了一个初学者 C++ 错误。希望有人知道它可能是什么。

谢谢!

编辑:作为参考,这里是 polytree (http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/PolyTree/_Body.htm) 的文档页面

edit:我认为 Clipper 库不是开源的,但它是。这是代码

typedef std::vector< IntPoint > Path;
typedef std::vector< Path > Paths;
class PolyNode;
typedef std::vector< PolyNode* > PolyNodes;

class PolyNode 
{ 
public:
    PolyNode();
    Path Contour;
    PolyNodes Childs;
    PolyNode* Parent;
    PolyNode* GetNext() const;
    bool IsHole() const;
    bool IsOpen() const;
    int ChildCount() const;
private:
    unsigned Index; //node index in Parent.Childs
    bool m_IsOpen;
    JoinType m_jointype;
    EndType m_endtype;
    PolyNode* GetNextSiblingUp() const;
    void AddChild(PolyNode& child);
    friend class Clipper; //to access Index
    friend class ClipperOffset; 
};

class PolyTree: public PolyNode
{ 
public:
    ~PolyTree(){Clear();};
    PolyNode* GetFirst() const;
    void Clear();
    int Total() const;
private:
    PolyNodes AllNodes;
    friend class Clipper; //to access AllNodes
};

【问题讨论】:

  • 如果没有 ClipperLib::PolyTree 的定义,我们只能猜测。但是请注意,您是按值返回的,因此将涉及一些复制构造函数(隐式或显式)。
  • @jsantander 如果执行了复制省略,则不会,对于此示例而言,这看起来很可能。 RVO 是c++ 中的少数例外之一,当程序的可观察行为可以随着优化而改变时。不过可能是这样。我想知道 OP 是否尝试过摆弄优化标志。
  • 不相关,但您也应该通过 const 引用传递 trianglesconst std::vector&lt;Triangle&gt;&amp; triangles
  • @user3281410 - 请发布多树定义。一旦你这样做了,那么你会得到任何关于它的问题的答案。目前,我们不知道如果没有实现用户定义的复制构造函数和赋值运算符,它是否可以安全复制。
  • 您可以粘贴未缩进的代码,选择它,然后单击“代码”按钮(看起来像大括号{ }),它会将整个选择缩进4个空格。注意标签。他们把缩进弄脏了。

标签: c++ clipperlib


【解决方案1】:

在做任何事情之前,请确保以下程序正常运行:

int main()
{
   PolyTree p1;
   // fill PolyTree with some values that make sense (please add code to do this)
   //...
   PolyTree p2 = p1; 
   PolyTree p3;
   p3 = p1;
}

这基本上就是我们想要测试的。如果您可以使此代码正常工作(添加相关的标头和必要的初始化),那么您可以重新关注该函数。如果上面的代码不起作用,那么这就是你的答案。

您需要获取上面的代码以产生正确的复制语义,甚至重要的是,当 main() 退出时,在 p1、p2 和 p3 的销毁时不会发生内存损坏。

因此,您可以修复课程以安全复制,或者忘记它并使用您必须非常小心且在有限情况下处理的课程(即您无法可靠地返回它的副本,因为您正在做现在)。

【讨论】:

  • 好吧,但请记住 PolyTree 是一个库类,OP 希望尽可能避免修改它。
  • 好的。但它让 OP 有机会准确了解拥有安全可复制对象所需的条件。
  • 当然,我不是在批评你的回答,只是想提一下:)
  • 谢谢保罗。我还不了解 C++ 的所有复杂性,但我认为这里的想法是测试复制分配和移动分配是否在这里工作?我会尝试一下,但是您是否希望编译器不允许这些语句之一编译?想法是编译器由于其不寻常的析构函数而无法自动生成对象的复制分配或移动分配?
  • 好的,我试过了。它编译了,看起来 p2 和 p3 都设置得很好。但是,当我从我所在的函数返回时,我在局部变量 p1、p2 或 p3 之一的 Clear() 命令中遇到了访问冲突错误(我不确定如何判断是哪个)。查看jsantander 的回答,我猜析构函数都试图多次删除相同的信息。
【解决方案2】:

记录在案,并结合对问题的冗长讨论中的所有回答。 问题是:

  1. 返回的值是一个超出范围的局部变量。这会调用 PolyTree 析构函数
  2. PolyTree 包含一个 PolyNode * 指针向量。这些是在调用 clipper.Execute() 时分配的。
  3. 但是 PolyTree::Clear() 确实删除了节点...并且 Clear() 由析构函数调用。
  4. 所以在函数内部,内容是正确的(由 Execute() 分配),当传递到外部时,在没有复制构造函数和operator= 的情况下,调用局部变量的析构函数并清除节点,在函数之外收到的结果是空的。

PolyTree::Clear() 的代码

void PolyTree::Clear() 
{
for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i)
      delete AllNodes[i];
    AllNodes.resize(0); 
    Childs.resize(0);
}

也许你应该遵循 Execute 的模式并将你的函数定义为:

void MeshHandler::trianglesToPolyTreeUnion(std::vector<Triangle> triangles,ClipperLib::PolyTree &tree) 

【讨论】:

  • 谢谢。我希望我能接受多个答案,但我已经承诺在我的评论中接受致命吉他。
【解决方案3】:

假设您不想修改(显然设计不佳的) Clipper 库,您可以按照我在评论中的建议进行操作:

// Make sure to have this at the top of your header file:
#include <memory>

std::unique_ptr<ClipperLib::PolyTree> MeshHandler::trianglesToPolyTreeUnion(std::vector<Triangle> triangles)
{
    // Rest of your code...

    std::unique_ptr<ClipperLib::PolyTree> tree(new ClipperLib::PolyTree);
    clipper.Execute(ClipperLib::ClipType::ctUnion, *tree);
    return tree;
}

然后,在调用你的函数时:

std::unique_ptr<ClipperLib::PolyTree> tree(yourMeshHandler.trianglesToPolyTreeUnion(/*...*/);

// make use of tree...

不过,我还是建议您开一张票(如果有错误跟踪器)或就这个问题联系图书馆的作者。

【讨论】:

  • 我也同意 lethal-guitar 联系作者。如果有的话,作者应该禁用复制和赋值,这样至少在尝试复制时会产生编译器(或链接器)错误。
【解决方案4】:

是否已经有解决此问题的方法?我正在处理同样的问题。 仍然没有运气。 polytree 只输出内存地址。

使用时:qDebug()Childs;

当我们有 2 个孩子时,终端的输出是: std::vector(0x55f30d2a91b0, 0x55f30d258480)

我希望有人知道如何解决这个问题..

【讨论】:

  • 这并没有提供问题的答案。您可以搜索类似的问题,或参考页面右侧的相关和链接问题以找到答案。如果您有一个相关但不同的问题,请提出一个新问题,并包含指向该问题的链接以帮助提供上下文。见:Ask questions, get answers, no distractions
【解决方案5】:

您的问题在 trianglesToPolyTreeUnion 底部的第三行。您正在创建的树是在堆栈上创建的,并且仅在函数的范围内。

你应该动态分配内存并返回一个指向树的指针,或者让你的树对象成为一个类成员,这样一旦函数返回它仍然在作用域内。

【讨论】:

  • 没有。那不是问题。 如果对象具有正确的复制语义,则按值返回对象是完全可以的。
猜你喜欢
  • 2021-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-29
  • 2021-01-30
  • 1970-01-01
  • 2021-09-28
  • 2014-07-18
相关资源
最近更新 更多