C++ C++;,为什么在修改新分配的对象后会出现访问冲突?

C++ C++;,为什么在修改新分配的对象后会出现访问冲突?,c++,memory-management,C++,Memory Management,好的,我现在正在做一个游戏,在今天重构了一些代码之后遇到了内存问题 它使用基于组件的设计,我正在修改如何分配组件并将其传递给实体。最初,一些组件被分配为实体中的成员变量,但现在我希望将它们分配到其他地方,并通过指针传递给实体 您可以在下面看到我是如何使用项目中的示例代码实现这一点的。我基本上遍历所有实体并为它们分配组件。问题是我在第6次迭代中“启动”instanceObject的第一行遇到了访问冲突,我不知道为什么。使用调试器,看起来没有任何变量指向无效地址 下面是我创建实体和组件的步骤 for

好的,我现在正在做一个游戏,在今天重构了一些代码之后遇到了内存问题

它使用基于组件的设计,我正在修改如何分配组件并将其传递给实体。最初,一些组件被分配为实体中的成员变量,但现在我希望将它们分配到其他地方,并通过指针传递给实体

您可以在下面看到我是如何使用项目中的示例代码实现这一点的。我基本上遍历所有实体并为它们分配组件。问题是我在第6次迭代中“启动”instanceObject的第一行遇到了访问冲突,我不知道为什么。使用调试器,看起来没有任何变量指向无效地址

下面是我创建实体和组件的步骤

for (unsigned int i = 0; i < 512; ++i) {
    InstanceObject* _pInstanceObject = new InstanceObject;

    // Initialize temporary variables
    XMFLOAT3 _position, _rotation;
    float _angle = (i / 512.0f) * (2.0f * XM_PI),
          _scale = (float)pResourceManager->numberGenerator.GetInt(50, 5);

    _position.x = 100000.0f * cos(_angle) + pResourceManager->numberGenerator.GetInt(50000, -25000);
    _position.y =(float) pResourceManager->numberGenerator.GetInt(50000, -25000);
    _position.z = 100000.0f * sin(_angle) + pResourceManager->numberGenerator.GetInt(50000, -25000);

    _rotation.x = (XM_PI * 2) * (pResourceManager->numberGenerator.GetInt(100, 0) / 100.0f);
    _rotation.y = (XM_PI * 2) * (pResourceManager->numberGenerator.GetInt(100, 0) / 100.0f);
    _rotation.z = (XM_PI * 2) * (pResourceManager->numberGenerator.GetInt(100, 0) / 100.0f);

    // Set component's state using the temporary variables.
    _pInstanceObject->StartUp(&_position,
        &_rotation,
        &XMFLOAT3(_scale, _scale, _scale),
        &XMFLOAT3(0.0f, 0.0f, 1.0f),
        &XMFLOAT3(1.0f, 0.0f, 0.0f),
        &XMFLOAT3(0.0f, 1.0f, 0.0f)
        );

    // Hand pointer of the component to entity.
    // Entity will handle deallocating component
}

知道是什么导致了这种行为吗?我应该如何修复它?

看起来XMFLOAT3结构是在堆栈上分配的,而不是在堆上。当它们的变量超出范围时,它们将被清理

请尝试在堆上分配结构:

XMFLOAT3* _position = new XMFLOAT3;

你在抄临时工的地址!这是不允许的

// Set component's state using the temporary variables.
_pInstanceObject->StartUp(&_position,
    &_rotation,
    &XMFLOAT3(_scale, _scale, _scale), // not valid
    &XMFLOAT3(0.0f, 0.0f, 1.0f), // not valid
    &XMFLOAT3(1.0f, 0.0f, 0.0f), // not valid
    &XMFLOAT3(0.0f, 1.0f, 0.0f) // not valid
    );
我建议您在编译器设置中提高警告级别,并尽可能多地启用标准一致性。当你的编译器告诉你做错了什么时,事情就容易多了

作为解决方案,您可以尝试简单地按值传递对象:

_pInstanceObject->StartUp(_position,
    _rotation,
    XMFLOAT3(_scale, _scale, _scale),
    XMFLOAT3(0.0f, 0.0f, 1.0f),
    XMFLOAT3(1.0f, 0.0f, 0.0f),
    XMFLOAT3(0.0f, 1.0f, 0.0f)
    );


bool StartUp(XMFLOAT3 _position, XMFLOAT3 _rotation, XMFLOAT3 _scale,
    XMFLOAT3 _lookAxis, XMFLOAT3 _strafeAxis, XMFLOAT3 _upAxis);

我认为问题在于InstanceObject类中的XMVECTOR需要16字节对齐,而新的操作符不能保证这一点。您可以在代码中添加一个快速检查,以确认-检查InstanceObject指针&0xF在崩溃的迭代中是否为非零

如果是这种情况,您可以编写一个自定义分配器来保证正确的对齐,并使用新的布局

当XMVECTOR作为成员使用时,这似乎是一个相当常见的问题(这里有一个关于这个问题的现有问题,如果您搜索的话,会有一些网页)

如果您只是想快速修复它,以便继续处理其他事情,可以向类声明中添加一个静态运算符new和delete,实现如下代码段:

void* InstanceObject::operator new( size_t size )
{
    // _aligned_malloc is a Microsoft specific method (include malloc.h) but its
    // straightforward to implement if you want to be portable by over-allocating
    // and adjusting the address
    void *result = _aligned_malloc( size, 16 );
    if( result )
        return result;

    throw std::bad_alloc(); 
}

void InstanceObject::operator delete( void* p )
{
    if( p ) _aligned_free(p);
}

如果
InstanceObject
共享一个共同的生命周期,那么您可以使用自己的对齐arena分配器来替代使用_aligned_malloc,并使
delete
成为无法提高效率的选项。

代码中没有一个东西肯定会导致问题,那就是XMLoadFloat3的构造函数(或者它是一个函数?)这需要一个XMFLOAT3*(它不存储该指针,是吗?)
InstanceObject
的复制构造函数与编译器免费生成的构造函数类似。你可以删除它,你会得到同样的行为。我不明白为什么会这样。它将一个由3个浮点组成的数组加载到XMVECTOR中,XMVECTOR是一种特殊的数据类型,它使用SIMD寄存器或其他东西。()是的,我继续明确声明了复制构造函数,这样我就可以排除这个问题。传递
XMFLOAT3 const&
不是更有效吗?@AzzA:可能,可能不是。请注意,函数将把它存储在类中。我不敢预测一个未公开的编译器将如何优化它。我很确定这不是问题所在。我在加载XMVECTORs时一直使用这种方法。只要XMFLOAT3在加载时在堆栈上完好无损,就可以了。也就是说,我尝试通过值传递,但没有运气。我相信只有Visual Studio会接受这样的代码,而且只要指针没有保留,就完全安全了-就像引用一样。你的意思是,如果定义了移动构造函数,按值传递临时值会更有效吗?我不认为这会有什么不同。xmfloat3将立即加载到XMVECTORs中。除非加载xmfloat时有我不知道的功能性副作用。
new
应该返回与对象正确对齐的内存,但可能编译器在这方面没有正确处理SSE类型。这不是第一次了。到目前为止,你的答案似乎是正确的。“坏”迭代没有得到16字节对齐的指针。我将休息一下,不再整天盯着这个问题,然后我将测试一个自定义分配器。我可以用uu declspec(align(16))宏声明成员XMVECTOR变量吗?我以前从未需要或编写过自定义分配器。
void* InstanceObject::operator new( size_t size )
{
    // _aligned_malloc is a Microsoft specific method (include malloc.h) but its
    // straightforward to implement if you want to be portable by over-allocating
    // and adjusting the address
    void *result = _aligned_malloc( size, 16 );
    if( result )
        return result;

    throw std::bad_alloc(); 
}

void InstanceObject::operator delete( void* p )
{
    if( p ) _aligned_free(p);
}