创建临时文件时std :: function错误的内存访问

时间:2019-01-24 13:08:29

标签: c++ c++11 segmentation-fault geometry

我目前正在实现一些抽象来表示3D对象的水平集操作。基本上,this中有关GLSL着色器的页面中描述的内容。

为了简要概述,可以通过将R ^ 3域映射到标量的一种函数来描述3D对象,该标量称为水平集(或有符号距离函数)。例如,对于一个球,水平集函数由phi(X) = X.Norm2() - R*R定义,其中Norm2表示R ^ 3中向量的平方欧几里德范数。

因此,我提出了一个LevelSetObject类,它代表了这样的概念:

 class LevelSetObject
    {

    using SDFFunction = std::function<double(double, double, double)>;

    protected:
        SDFFunction m_SDF;

    public:

        double SDF(double x, double y, double z) const {
            return m_SDF(x, y, z);
        }

现在,我想在LevelSetObject之间定义几个运算符。例如 union 运算符:

LevelSetObject LevelSetObject::operator+(const LevelSetObject& other) const {
        LevelSetObject outObj;
        outObj.m_SDF = [this, other]
            (double x, double y, double z) {
                return std::min(m_SDF(x, y, z), other.m_SDF(x, y, z));
            };
        return outObj;
    }

但是当我创建一个临时文件时,由于例如三重和,我遇到了错误的内存访问(而如果像在注释的情况下那样对两个对象分别求和,则使用Valgrind而不是SIGSEV不会发现内存泄漏)。 LevelSetSphereLevelSetObject的派生类,其中我专门定义了球体的SDF(根据其center和其radius

int main(int argc, char* argv[]) {

    // Generate the central sphere
    double radius = 1.0;
    SimpleVector center(2, 2, 2);
    LevelSetSphere sphere(radius, center);

    // Generate the ears spheres
    LevelSetSphere ear1(radius/2, SimpleVector(1, 1, 2));
    LevelSetSphere ear2(radius/2, SimpleVector(3, 1, 2));

    // Combine objects
    auto mickeyMouse = sphere + ear1 + ear2;
    //auto first = sphere + ear1;
    //auto mickeyMouse = first + ear2;

    // Materialize in the domain

    mickeyMouse.SDF(0.0, 0.0, 0.0);



}

我想是在operator+定义中,std::function保留了对other的引用,当我实际调用m_SDF时,该引用变成了悬空引用,因为在三重和期间创建。我还尝试将operator+的签名更改为operator+(const LevelSetObject other),因此通过副本传递,但结果是相同的。

我在哪里失败? :)

2 个答案:

答案 0 :(得分:10)

派生类中的lambda将this捕获到派生类中,并将其推入基类的std::function中。

这是麻烦的秘诀。

这意味着,至少派生类必须完全为Rule of 3 compliant,并至少实现一个复制构造函数和一个赋值操作符,该操作符将用新的lambda精心安装。捕获的this实际上是指派生类的正确实例。

如果您有一个std::function类的成员,它捕获了自己的this,并且该类被复制,则捕获的this不会自动更新以引用新的类的实例。 C ++无法以这种方式工作。新类的std::function的{​​{1}}仍引用该类的原始实例。而且,如果某个类的实例是从该类的另一个实例分配的,那您猜怎么着?复制的this捕获的std::function仍指向该类的复制实例。

但是,我在这里实际上看不到this所做的任何无法由花园变种的虚函数实现的事情。只需将std::function替换为虚拟函数,整个头痛就消失了。

答案 1 :(得分:7)

您对内存的不良访问不是由于other变量引起的,它是临时对象的this指针超出范围。

您可以通过显式捕获SDF变量来解决此问题,如下所示:

LevelSetObject LevelSetObject::operator+(const LevelSetObject& other) const {
        LevelSetObject outObj;
        auto& SDF=this->m_SDF;
        auto& other_SDF=other.m_SDF
        outObj.m_SDF = [SDF, other_SDF]
            (double x, double y, double z) {
                return std::min(SDF(x, y, z), other_SDF(x, y, z));
            };
        return outObj;
    }