【问题标题】:Why does this Object wonk out & get deleted?为什么这个对象会消失并被删除?
【发布时间】:2010-04-10 08:11:52
【问题描述】:

单步调试,BBox对象在函数入口处是没问题的,但是一进入函数,vfptr对象就指向0xccccc。我不明白。

  1. 这是什么原因造成的?
  2. 当对象不是从其他类派生时,为什么那里有一个虚拟表引用。 (虽然,它驻留在我的 Player 类继承的 GameObject 中,我从玩家内部检索 BBox。但是,为什么 BBox 有引用?不应该是应该在该引用中维护的玩家吗?)

对于 1;一些代码供参考:

A.我从玩家那里检索边界框。这将按预期返回一个边界框。然后我将它的地址发送到 GetGridCells。

 const BoundingBox& l_Bbox = l_pPlayer->GetBoundingBox();

 boost::unordered_set < Cell*, CellPHash >& l_GridCells = GetGridCells ( &l_Bbox ); 

B.这就是 a_​​pBoundingBox 发疯并获得垃圾值的地方。

 boost::unordered_set< Cell*, CellPHash > CollisionMgr::GetGridCells(const BoundingBox *a_pBoundingBox)
 {

我认为下面的代码也是相关的,所以我还是把它贴在这里:

 const BoundingBox& Player::GetBoundingBox(void)
 {
 return BoundingBox( &GetBoundingSphere() );
 }

 const BoundingSphere& Player::GetBoundingSphere(void)
 {
 BoundingSphere& l_BSphere = m_pGeomMesh->m_BoundingSphere;

 l_BSphere.m_Center = GetPosition();

 return l_BSphere;
 }

 // BoundingBox Constructor
 BoundingBox(const BoundingSphere* a_pBoundingSphere);

谁能告诉我为什么会这样?另外,如果您希望我发布更多代码,请告诉我。

谢谢!

【问题讨论】:

  • 你所说的“出局”是什么意思?
  • @Eli: 对象在到达 GetGridCells(..) 时超出了范围

标签: c++ memory


【解决方案1】:
 const BoundingBox& Player::GetBoundingBox(void)
 {
 return BoundingBox( &GetBoundingSphere() );
 }

在这里,您将返回对临时 BoundingBox 对象的引用。只要return 语句结束,该对象就会超出范围。

返回 BoundingBox 而不是 BoundingBox&amp;


还有:

 BoundingSphere& l_BSphere = m_pGeomMesh->m_BoundingSphere;

 l_BSphere.m_Center = GetPosition();

在这里,您引用m_pGeomMesh 的边界球体,然后修改它所引用的值。这将导致对原始对象的修改。你确定这是你想要的吗?


还有:

 // BoundingBox Constructor
 BoundingBox(const BoundingSphere* a_pBoundingSphere);

在使用引用非常有意义的唯一地方,您使用指针代替。为什么?

【讨论】:

  • 但它返回一个常量引用。在持有引用的调用者超出范围之前,是否不能保证对象有效?我同意如果它是非 const 引用会出现问题。但我依稀记得 const 引用有特殊规则。
  • 确实有这样的规定,但是这里不适用。对象的const 引用在return 语句中创建,然后复制为返回值,之后原始const 引用超出范围并且对象被销毁。 (其他人可能会在技术细节上纠正我,但我很确定这个 const 参考不会延长任何内容的范围。)
  • 托马斯是对的。如果函数返回一个临时的并且该临时绑定到引用,那么生命周期将被延长。但在这种情况下,临时是函数内部的。考虑一下,如果函数在两个不同的翻译单元中,编译器不可能知道函数返回的引用是否是临时的。
  • 感谢您的澄清..现在知道了。
  • 感谢大家帮助我。 @Thomas: BoundingSphere& l_BSphere = m_pGeomMesh->m_BoundingSphere; l_BSphere.m_Center = GetPosition(); // 是的,我想翻译球体..所以我知道这会修改原始对象。 // BoundingBox 构造函数 BoundingBox(const BoundingSphere* a_pBoundingSphere);我经常需要转换 btw BBox 和 BSphere,因此如果我将其作为参考,我会遇到头文件中循环依赖的问题。即使我使用了包含保护,我也需要将它们中的每一个都包含在另一个中。你能建议我一个更好的方法来解决这个问题吗?
【解决方案2】:
  1. 正如 Thomas 所说,BoundingBox 是由 return 语句创建的,并在 return 语句的末尾被销毁,因为它是临时的。返回一个 const 引用与分配给一个持久的 const 引用不同(它确实延长了一个临时引用)。由于复制省略,返回一个值并将其分配给调用者范围内的某个对象没有开销,尽管您可能必须定义一个复制构造函数。
  2. 如果类具有任何虚方法(例如析构函数),则会创建 vtable。如果您有覆盖这些方法的派生类,则需要基本 vtable 来找到正确的覆盖。所有这些都可能发生在另一个编译单元中,因此编译器不太可能对其进行优化。

【讨论】:

  • 第一点是对的,但是第二点我已经读了三遍了,还是看不懂。
  • 我理解关于第一点的第一个答案。关于第二个答案,我有一个问题:(但在此之前,这里有一些背景) - Player 继承 GameObject(& 覆盖析构函数/更新/Render 方法) - GameObject 包含 BoundingSphere 现在,我知道它需要 vTable 来解析正确的类。但是,为什么/如何将这些信息存储在 BoundingSphere 中。我会假设 vtable 只能在 Player 对象中找到。请在这个问题上纠正我。谢谢
  • @brainy:BoundingSphere 不应该仅仅因为它在某个类中创建一个成员就包含一个 vtable。 virtual 必须在其定义中的某处使用。随意在您的问题中发布更多代码。
  • @Potatocorn :啊,是的!你是对的。我有将析构函数设为虚拟的习惯,在你提到之后我才意识到这一点。我认为这可以解释 vtable 的存在。 类 BoundingSphere;类 BoundingBox { public: BoundingBox(const BoundingSphere* a_pBoundingSphere); //BoundingBox( const BoundingBox&amp; rhs);虚拟〜边界框(无效);常量 std::vector GetCorners() 常量; glm::vec3 m_Center; glm::vec3 m_Extents; // 位于中心的边界框沿 X、Y、Z 轴的范围 };
  • Grr..如何在 cmets 中正确格式化代码,以便正确显示?
【解决方案3】:

@Thomas:这里有 m 个类:

#ifndef BOUNDINGBOX_H
#define BOUNDINGBOX_H

class BoundingSphere;

class BoundingBox
{
public:

    BoundingBox(const BoundingSphere* a_pBoundingSphere);
    //BoundingBox( const BoundingBox& rhs);
    virtual ~BoundingBox(void);

    const std::vector< glm::vec3 > GetCorners() const;

    glm::vec3 m_Center;
    glm::vec3 m_Extents;        // extents along the X, Y, Z axis for the bounding box positioned at the center
};

#endif

#ifndef BOUNDINGSPHERE_H
#define BOUNDINGSPHERE_H

class BoundingBox;

class BoundingSphere
{
public:
    BoundingSphere();

BoundingSphere(const BoundingBox* a_pBoundingBox);

    BoundingSphere(const glm::vec3& a_Center, const float& a_Radius);
    virtual ~BoundingSphere(void);

    // Access the Center
    const glm::vec3 &GetCenter(void) const  { return(m_Center);     };
    void SetCenter(const glm::vec3 &Center) { m_Center = Center;    };

    // Access the Radius
    float GetRadius(void) const     { return(m_Radius);     };
    void SetRadius(float Radius)    { m_Radius = Radius;    };

    glm::vec3 m_Center;
    float m_Radius;
};

#endif

现在,我理解这一点的方式是:如果在 BoundingBox 的构造函数中有对 BoundingSphere 的引用而不是指针,即

BoundingBox(const BoundingSphere& a_pBoundingSphere);

它需要 BoundingSphere 的定义在编译时可用。因此,我必须在 BoundingBox.h 中包含 BoundingSphere.h 以使定义可用。反之亦然,因此会产生循环引用。

请在这方面纠正我..

谢谢

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-25
    • 2010-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-06
    相关资源
    最近更新 更多