std::函数 创建临时内存时内存访问错误

std::function bad memory access when creating a temporary

本文关键字:内存 错误 访问 函数 创建 std      更新时间:2023-10-16

我目前正在实现一些抽象来表示 3D 对象的级别集操作。基本上,这个惊人的GLSL着色器页面中描述了什么。

为了给出一个简短的概述,一个 3D 对象可以用一个函数来描述,该函数将 R^3 域映射到称为水平集(或有符号距离函数)的标量。例如,对于球体,水平集函数由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);
}

现在我想定义几个介于LevelSetObjects 之间的运算符。例如,联合运算符:

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 没有发现内存泄漏)。LevelSetSphere是一个派生的LevelSetObject类,其中我专门定义了球体的 SDF(给定其centerradius)

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),所以通过复制,但结果是一样的。

我哪里失败了?:)

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

这是麻烦的秘诀。

这意味着,派生类至少必须完全符合 Rule of 3,并且至少实现一个复制构造函数和一个赋值运算符,该运算符会精心重新安装新的 lambda,并使用实际引用派生类的正确实例的新捕获this

如果你有一个类的std::function成员,它捕获了它自己的this,并且该类被复制,则捕获的this不会自动更新以引用该类的新实例。 C++不是这样工作的。新类的std::functionthis仍然引用该类的原始实例。如果一个类的实例是从该类的另一个实例分配的,你猜怎么着?复制的std::function捕获的this仍然指向该类的复制自实例。

但我真的没有看到std::function在这里所做的任何不能通过花园式虚拟函数实现的事情。只需用虚拟功能替换m_SDF,整个头痛就会消失。

内存访问错误不是由于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;
}