【问题标题】:vector push_back causes assertion error but list push_back works矢量 push_back 导致断言错误,但列表 push_back 有效
【发布时间】:2014-01-23 14:58:02
【问题描述】:

我正在使用为 Visual Studio 2008 编译的 Qt5.1。

我正在尝试渲染具有多个纹理的对象。在我的原始代码中,我使用了一个网格类来管理加载 3D 对象,它基于本教程:

http://ogldev.atspace.co.uk/www/tutorial22/tutorial22.html

网格类包含一个保存纹理的向量。纹理从文件中加载图像数据并跟踪渲染自身所需的所有 OpenGL 状态管理。

尝试使用 Qt 向我的网格类矢量容器添加多个纹理时,我收到“调试断言失败:_BLOCK_TYPE_US_VALID(pHead->nBlockUse)”错误。当向向量添加纹理时调用析构函数时会出现此错误,类似于此线程中描述的内容:

Destructor called on object when adding it to std::list

我是 Qt 新手,想知道在这种情况下处理纹理的最佳方法是什么。教程代码使用 GLEW 而不是 Qt,我必须从 QOpenGLFunctions 继承来管理渲染纹理的 OpenGL 状态(我的网格类也继承自 QOpenGLFunctions)。似乎默认的复制构造函数正在对纹理对象进行浅拷贝,这会删除由 Qt 管理的内存。但是,当我切换到对纹理使用列表容器时,我没有收到断言错误。

这是我的代码的简化,说明了我的问题:

#include <QOpenGLFunctions_3_3_Core>
#include <list>
#include <vector>

class Texture : protected QOpenGLFunctions_3_3_Core
{

public:
    Texture() 
    { 
    }

    ~Texture()
    {
        std::cout << "destructor" << std::endl;
    }
};

std::vector<Texture> textureList; //this causes the assertion failure
//std::list<Texture> textureList; //this works
void tester()
{

    for (int i = 0; i < 3; i++)
    {
        Texture Texture;
        textureList.push_back(Texture);
    }
}

int main()
{
    tester();

    //Qt application code
}

【问题讨论】:

  • Texture 的非简化版本是否在内部维护了任何动态成员(即您的 new-ing 和 delete-ing)?我提前为我不太了解 qt 道歉,但 QOpenGLFunctions_3_3_Core 是否有内置的 Rule of Three 支持(如果上述关于动态成员管理的问题的答案是“是”,那么你的 Texture 衍生产品也是如此)?
  • 我没有在我的纹理类中做任何动态内存管理。我加载图像数据,将其发送到 OpenGL 目标,并在渲染时绑定纹理。在此示例中,如果 Texture 类没有从 QOpenGLFunctions_3_3_Core 继承,那么我不会收到断言错误。看起来 QOpenGLFunctions 不支持三法则。由于 QOpenGLFunctions 是一个跟踪 OpenGL 上下文的复杂对象,也许我需要传递一个指向 QOpenGLFunctions 单个实例的指针,而不是尝试在 Texture 和我的 Mesh 类中继承它(此处未显示) )?
  • 我见过的几个例子都有一个继承自 QOpenGLFunctions 的类,但是当事情变得更复杂时,我不确定 Qt 的正确用法是什么。

标签: c++ qt opengl


【解决方案1】:

这个问题来自vectorlist内部实现的不同。

首先,list 是双向链表,所以list 可以在没有原始元素副本的情况下增长。但是vector 是动态分配的数组。如果vector 的容量已满,vector 分配更大尺寸的新数组(Array doubling),并将元素从旧数组复制到新数组。下面的 sn-p 显示了断言发生的原因。 (也许)

#include <iostream>
#include <list>
#include <vector>

class Texture
{

public:
  Texture() 
  {   
    std::cout << "constructor" << std::endl;
  }   
  Texture(const Texture &t) 
  {   
    std::cout << "copy constructor" << std::endl;
  }   

  ~Texture()
  {   
    std::cout << "destructor" << std::endl;
  }   
};

std::vector<Texture> textureList;
//std::list<Texture> textureList;
void tester()
{
  for (int i = 0; i < 3; i++)
  {   
    //textureList.reserve(4);
    std::cout << "Loop\n";
    Texture texture;
    textureList.push_back(texture);
    std::cout << "Capacity: " << textureList.capacity() << "\n";
  }   
  std::cout << "End\n";
}

int main()
{
  std::cout << "main\n";
  tester();
  std::cout << "main end\n";
}

结果

main
Loop
constructor
copy constructor
Capacity: 1
destructor
Loop
constructor
copy constructor
copy constructor
destructor
Capacity: 2
destructor
Loop
constructor
copy constructor
copy constructor
copy constructor
destructor
destructor
Capacity: 4
destructor
End
main end
destructor
destructor
destructor

只要向量长大,纹理实例就会被销毁。

如果你想避免这个问题,请使用vector::reserve(检查上面的代码:textureList.reserve(4))。但是我认为这段代码会出现另一个问题,所以我建议使用纹理指针而不是纹理实例来处理。

【讨论】:

  • QOpenGLFunctions_3_3_Core 似乎不支持三法则。我将指向QOpenGLFunctions_3_3_Core 的指针传递给Texture 类,而不是从它继承来设置OpenGL 状态。即使使用Textures 的向量,它也不再使断言失败。有关向量和列表行为之间差异的其他详细信息很有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-03
  • 1970-01-01
  • 2022-01-26
  • 1970-01-01
相关资源
最近更新 更多