3

我正在不同系统之间转换一些代码,并且我有一个关于 c++ 向量的问题。

如果我做这样的事情:

在头文件中:

struct Vertex
{
    float x;
    float y;
    float z;
}

struct submesh
{
    Vertex *meshdata;
}

std::vector<submesh> meshes;

在 c++ 文件中的例程中:

{
    Vertex *data = new Vertex[1024];

    submesh g;
    g.meshdata = data;
    meshes.push_back(g);

    delete [] data;
}

我会遇到麻烦吗?我的假设是该向量将包含一个指向数据的指针,一旦我对其调用删除,该指针将不再有效。我是否需要为Vertex编写一个复制构造函数以便首先复制数据?

额外的:

问题更多与如何将指向已分配内存的指针放入 std::vector<> 并仍然清理本地分配的数据有关。本质上,我如何将数据复制到向量中,以便我仍然可以清理我的副本。

原始代码在 DirectX 中。我正在将它移植到 iPhone 上。原始代码在例程中本地分配了一个子网格,使用:

{
    ID3DXMesh* subMesh = 0;
    D3DXCreateMesh(SubGrid::NUM_TRIS, SubGrid::NUM_VERTS, D3DXMESH_MANAGED, elems, gd3dDevice, &subMesh));

    //
    // ... do some magical things to submesh
    //

    SubGrid g;
    g.mesh = subMesh;
    g.box  = bndBox;
    mSubGrids.push_back(g);
}

我试图复制 ID3DXMesh 如何能够添加到向量中,然后在例程中失去它的范围。

由于我无法访问 D3DXCreateMesh(),我想我只需分配我需要的顶点,将它们放入向量中,然后进行清理。

抱歉,我想保留细节,因为问题只是我如何分配一块数据,将指针放入 std::vector<>,然后清理本地分配的内存。:)

我假设必须在某处编写一个复制构造函数。只是不确定在哪里或如何。

子网格如下所示:

struct SubGrid
{
    ID3DXMesh* mesh;
    AABB box;

    // For sorting.
    bool operator<(const SubGrid& rhs)const;

    const static int NUM_ROWS  = 33;
    const static int NUM_COLS  = 33;
    const static int NUM_TRIS  = (NUM_ROWS-1)*(NUM_COLS-1)*2;
    const static int NUM_VERTS = NUM_ROWS*NUM_COLS;
};

他们添加到的向量看起来像:

std::vector<SubGrid> mSubGrids;
4

3 回答 3

1

是的,当然,vector 会有一个指向已删除内存的指针。您需要的是:

  1. submesh为(不是Vertex)创建复制构造函数。或

  2. 更改submesh为具有顶点数组(不仅仅是指针)。

复制构造函数可以这样完成:

struct submesh
{
    Vertex *meshdata;
    unsigned meshsize;
    submesh(Vertex* v = 0, unsigned s= 0) : meshdata(v), meshsize(s){}
    submesh(const submesh& s)
    {
        if(meshdata) /*we have stored data, delete it.*/ delete(meshdata);
        meshdata = new Vertex[s.meshsize];
        meshsize = s.meshsize;
        memcpy(meshdata, s.meshdata, sizeof(Vertex) * meshsize);
    }
};

当然,非常推荐使用 unique_ptr(如果您使用 c++11)或旧 c++ 的auto_ptr。尽可能避免内存管理的噩梦。

检查如何在 C++ 中使用指向动态分配对象的指针向量时避免内存泄漏?

于 2013-02-10T10:21:43.410 回答
1

您的代码在单线程应用程序中看起来不错。您的代码只分配data一次内存delete [] data

我是否需要为 Vertex 编写一个复制构造函数以便首先复制数据?

如您所示,您的代码很干净,meshes仅指向已分配data。如果您打算data在 call 时复制meshes.push_back(g),那么您的代码不会按照您的意图进行。

您可能想std::vector改用:

struct submesh
{
    std::vector<Vertex> meshdata;
}

vector<submesh> meshes;

void Func()
{
    meshes.emplace_back(submesh());
    meshes.at(0).meshdata.resize(100);
}

STL 容器使用RAII习惯用法,它会自动为您管理内存释放。

于 2013-02-10T10:30:29.927 回答
1

不要在不需要时直接动态分配,在这种情况下你不需要。由于您正在填充自己的子网格数据而不是使用ID3DXMesh,因此该数据的容器应该符合 RAII。如果我对此进行编码,我将submesh完全删除该类并仅使用:

// vector containing list of vertices.
typedef std::vector<Vertex> SubMesh;

然后,您的SubGrid类可以成为一个简单的容器,其中包含一个submesh集合作为其属性之一。我注意到您还有一个AABB用于盒子对象的类。你会继续把它藏在里面SubGrid。我不必在这里工作,所以我正在制作其中的一些,但类似于以下内容:

// a simple 3-value triplet of floats
struct Vertex
{
    float x,y,z;
};

// a Submesh is an arbitrary collection of Vertex objects.
typedef std::vector<Vertex> SubMesh;

// I'm defining AABB to be an 8-vertex object. your definition
//  is likely different, but I needed something to compile with =)
typedef Vertex AABB[8];

class SubGrid
{
public:
    SubGrid() {};

    // comparator for container ordering
    bool operator <(const SubGrid&);

    // submesh accessors
    void setSubmesh(const SubMesh& mesh) { submesh = mesh;}
    SubMesh& getSubmesh() { return submesh; }
    const SubMesh& getSubmesh() const { return submesh; }

    // box accessors
    AABB& getBox() { return box; }
    const AABB& getBox() const { return box;}

private:
    SubMesh submesh;
    AABB box;
};

// arbitrary collection of SubGrid objects
typedef std::vector<SubGrid> SubGrids;

将此添加到您的全局SubGrid集合g时,您有多种可能性。你可以这样做:

// declared globally 
Subgrids g;

// in some function for adding a subgrid item
SubGrid subgrid;
AABB& box = subgrid.getBox();
SubBesh& submesh = subgrid.getSubmesh();

// ... initialize your box and submesh data ...

g.push_back(subgrid);

但是你会复制很多数据。要加强内存访问,您总是可以这样做:

// push an empty SubGrid first, then set it up in-place
g.push_back(SubGrid());
Subgrid& subgrid = *(g.back());
AABB& box = subgrid.getBox();
SubMesh& submesh = subgrid.getSubmesh();

//... initialize your box and submesh data ...

这将建立对SubGrid刚刚添加到全局集合的引用,然后允许您就地修改它。这只是许多可能的设置选项之一。应该注意的是,如果您的工具链中有 C++11(并且如果您在 MacOS 或 iOS 上执行此操作,您可能会这样做,因为 Apple LLVM 4.2 的 clang 在 C++11 合规性方面非常好)这个明智地使用move-constructorsmove-assignment-operators可以提高效率。

最重要的是,不是一个new或被delete看到的。

无论如何,我希望这能给你一些想法。

于 2013-02-10T19:16:51.830 回答