C ++, почему я получаю нарушения прав доступа после изменения вновь выделенных объектов? - PullRequest
5 голосов
/ 20 января 2012

Хорошо, сейчас я работаю над игрой и столкнулся с проблемой памяти после рефакторинга некоторого кода сегодня.

Он использует дизайн на основе компонентов, и я модифицировал, как компоненты были распределены и переданы объектам. Первоначально некоторые компоненты были выделены как переменные-члены внутри сущностей, но теперь я хочу, чтобы они были размещены в другом месте и передавались сущности через указатель.

Ниже вы можете увидеть, как я это реализовал, с примером кода из моего проекта. Я по сути перебираю все сущности и выделяю для них компоненты. Проблема в том, что я сталкиваюсь с нарушением прав доступа в первой строке «запуска» instanceObject на 6-й итерации и не знаю почему. При использовании отладчика не похоже, чтобы какая-либо переменная указывала на неверный адрес.

Вот что я делаю для создания сущностей и компонентов.

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
}

А вот соответствующий код компонента.

class InstanceObject {
private:
    XMVECTOR anteriorAxis,
         lateralAxis,
         normalAxis,
         position,
         rotationQuaternion,
         scale;

    XMMATRIX translationMatrix,
         rotationMatrix,
         scaleMatrix;
    void SetAnteriorAxis(const XMFLOAT3 *_anteriorAxis) { anteriorAxis = XMLoadFloat3(_anteriorAxis); }
    void SetLateralAxis(const XMFLOAT3 *_lateralAxis)   { lateralAxis = XMLoadFloat3(_lateralAxis); }
    void SetNormalAxis(const XMFLOAT3 *_normalAxis)     { normalAxis = XMLoadFloat3(_normalAxis); }
public:
    InstanceObject(void)  { }
    InstanceObject(const InstanceObject& _object) : anteriorAxis(_object.anteriorAxis), lateralAxis(_object.lateralAxis),
        normalAxis(_object.normalAxis), position(_object.position), rotationQuaternion(_object.rotationQuaternion), scale(_object.scale),
        translationMatrix(_object.translationMatrix), rotationMatrix(_object.rotationMatrix), scaleMatrix(_object.scaleMatrix) {}
    ~InstanceObject(void) { }

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

    void SetPosition(const XMFLOAT3* _position) { position = XMLoadFloat3(_position); }
    void SetRotationQuaternion(const XMFLOAT3 *_rotation) { rotationQuaternion = XMQuaternionRotationRollPitchYaw(_rotation->x, _rotation->y, _rotation->z); }
    void SetScale(const XMFLOAT3 *_scale) { scale = XMLoadFloat3(_scale); }
}

bool InstanceObject::StartUp(const XMFLOAT3 *_position, const  XMFLOAT3 *_rotation, const XMFLOAT3 *_scale,
        const XMFLOAT3 *_lookAxis, const XMFLOAT3 *_strafeAxis, const XMFLOAT3 *_upAxis) {
    SetPosition(_position);
    SetRotationQuaternion(_rotation);
    SetScale(_scale);   
    SetAnteriorAxis(_lookAxis);
    SetLateralAxis(_strafeAxis);
    SetNormalAxis(_upAxis);

    return true;
}

Есть идеи, что может быть причиной такого поведения и как мне это исправить?

Ответы [ 3 ]

7 голосов
/ 20 января 2012

Я считаю, что проблема в том, что XMVECTOR в вашем классе InstanceObject должен быть выровнен на 16 байт, и новый оператор не гарантирует этого для вас. Вы можете добавить быструю проверку в свой код для подтверждения - проверьте, является ли указатель InstanceObject & 0xF ненулевым на итерации, в которой он падает.

Если это так, вы можете написать собственный распределитель, который гарантирует правильное выравнивание и использовать новое размещение.

Кажется, это довольно распространенная проблема с XMVECTOR при использовании в качестве участника (вот существующий Отчет об ошибке подключения по этой проблеме, есть несколько веб-страниц, если вы ищете).

Если вы просто хотите быстро исправить это, чтобы заняться другими делами, вы можете добавить статический оператор new и удалить к объявлению класса, реализованному что-то вроде следующего фрагмента:

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 имеет общее время жизни, вы можете заменить использование _aligned_malloc на свой собственный распределенный распределитель арены и сделать delete запретом для повышения эффективности.

4 голосов
/ 20 января 2012

Ты забираешь адреса временников! Это даже не разрешено!

// 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);
0 голосов
/ 20 января 2012

Похоже, что ваши структуры XMFLOAT3 размещаются в стеке, а не в куче. Они будут очищены, когда их переменные выйдут из области видимости.

Попробуйте вместо этого разместить свои структуры в куче:

XMFLOAT3* _position = new XMFLOAT3;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...