使用两次时功能代码会中断

发布于 2024-09-27 01:35:01 字数 3316 浏览 4 评论 0原文

我仍在研究我的 Field 课程,并试图提高我糟糕的插入/擦除性能。

然而,新功能只能运行一次,然后当我第二次使用它时就会发生灾难性的故障。

这是代码:

template <class T>
T *Field<T>::insert(const T *pPos, const T& data)
{
    // Special case:  field is empty. insert should still succeed.
    // Special case: Pointing to one past the end. insert should still succeed
    if( empty() || pPos == last() )
    {
        this->push_back(data);
        return (this->last() - 1);
    }

    /* Explanation:  Find cell before which to insert new value. Push_back new
      new value, then keep swapping cells until reaching *pPos and swapping it
      with data. The while fails, we exit, insert successful. */
    T *p = ( std::find( this->first(), this->last(), *pPos ));
    if( p != last() )
    {
        this->push_back(data);

        T *right = (this->last() - 1);
        T *left  = (this->last() - 2);
        while( *pPos != data )
            std::iter_swap( left--, right-- ); 

    // pPos *has* to be the destination of new value, so we simply return param.
        return const_cast<T*>(pPos);
    }
    else
        throw std::range_error("Not found");
}

main 调用代码

// Field already has push_back()ed values 10, 20, 30.
field->insert( &(*field)[2], 25 ); // field is a ptr (no reason, just bad choice)

在控制台上打印时生成此输出。

Field: 10 20 30    // Original Field
25                 // Function return value
Field: 10 20 25 30 // Correct insertion.

来自 main 的新调用代码

// Field already has push_back()ed values 10, 20, 30
field->insert( &(*field)[2], 25 );
field->insert( &(*field)[3], 35 );

在控制台上打印时生成此输出。

Field: 10 20 30
25
35
-4.2201...e+37, 10, 15, 20, 30

Windows has triggered a breakpoint in Pg_1.exe. 
This may be due to a corruption in the heap (oh shit).

No symbols are loaded for any call stack frame. 
The source code cannot be displayed.

然后控制台将不再关闭,直到我关闭 VSC++08 本身。

什么?为什么?如何?我的代码在做什么!?

附加信息

推球前场地大小为三人,容量为四人。两次插入后,Field 的容量正确增加到 8(加倍),并存储 5 个元素。

无论我使用 insert() 在哪里插入第二个元素,它都会以完全相同的方式失败。相同的输出,甚至第一个单元格的数字相同(我认为)。

附加代码

Push_Back()

注意:此代码在我的重构过程中没有更改。这个功能一直有效,所以我非常怀疑这将是问题原因。

/* FieldImpl takes care of memory management. it stores the values v_, vused_,
  and vsize_. Cells in the Field are only constructed when needed through a 
  placement new that is done through a helper function. */
template <class T>
void Field<T>::push_back(const T& data)
{
    if( impl_.vsize_ == impl_.vused_ )
    {
        Field temp( (impl_.vsize_ == 0) ? 1 
                                        : (impl_.vsize_ * 2) );

        while( temp.impl_.vused_ != this->impl_.vused_ )
            temp.push_back( this->impl_.v_[temp.size()] );

        temp.push_back(data);
        impl_.Swap(temp.impl_);
    }
    else
    {
// T *last()  const { return &impl_.v_[impl_.vused_]; }
// Returns pointer to one past the last constructed block.
// variant:     T *last()  const { return impl_.v_; }
        Helpers::construct( last(), data );
        ++impl_.vused_;
    }
}

I'm still working on my Field class, and tried to improve my piss-poor insertion/erase performance.

However, the new function works once, then breaks catastrophically when I use it a second time.

This is the code:

template <class T>
T *Field<T>::insert(const T *pPos, const T& data)
{
    // Special case:  field is empty. insert should still succeed.
    // Special case: Pointing to one past the end. insert should still succeed
    if( empty() || pPos == last() )
    {
        this->push_back(data);
        return (this->last() - 1);
    }

    /* Explanation:  Find cell before which to insert new value. Push_back new
      new value, then keep swapping cells until reaching *pPos and swapping it
      with data. The while fails, we exit, insert successful. */
    T *p = ( std::find( this->first(), this->last(), *pPos ));
    if( p != last() )
    {
        this->push_back(data);

        T *right = (this->last() - 1);
        T *left  = (this->last() - 2);
        while( *pPos != data )
            std::iter_swap( left--, right-- ); 

    // pPos *has* to be the destination of new value, so we simply return param.
        return const_cast<T*>(pPos);
    }
    else
        throw std::range_error("Not found");
}

Calling code from main

// Field already has push_back()ed values 10, 20, 30.
field->insert( &(*field)[2], 25 ); // field is a ptr (no reason, just bad choice)

Produces this output when printed on the console.

Field: 10 20 30    // Original Field
25                 // Function return value
Field: 10 20 25 30 // Correct insertion.

New calling code from main

// Field already has push_back()ed values 10, 20, 30
field->insert( &(*field)[2], 25 );
field->insert( &(*field)[3], 35 );

Produces this output when printed on the console.

Field: 10 20 30
25
35
-4.2201...e+37, 10, 15, 20, 30

Windows has triggered a breakpoint in Pg_1.exe. 
This may be due to a corruption in the heap (oh shit).

No symbols are loaded for any call stack frame. 
The source code cannot be displayed.

The console then proceeds to never shutdown again until I close VSC++08 itself.

What? Why? How? What is my code doing!?

Additional Info

The Field has a size of three before the push, and a capacity of four. After two insertions, the Field is correctly increased to have a capacity of 8 (doubled), and stores five elements.

It doesn't matter where I insert my second element with insert(), it will fail the exact same way. Same output, even same number (I think) at the first cell.

Additional Code

Push_Back()

Note: This code was not changed during my refactoring. This function has always worked, so I highly doubt that this will be the problem-cause.

/* FieldImpl takes care of memory management. it stores the values v_, vused_,
  and vsize_. Cells in the Field are only constructed when needed through a 
  placement new that is done through a helper function. */
template <class T>
void Field<T>::push_back(const T& data)
{
    if( impl_.vsize_ == impl_.vused_ )
    {
        Field temp( (impl_.vsize_ == 0) ? 1 
                                        : (impl_.vsize_ * 2) );

        while( temp.impl_.vused_ != this->impl_.vused_ )
            temp.push_back( this->impl_.v_[temp.size()] );

        temp.push_back(data);
        impl_.Swap(temp.impl_);
    }
    else
    {
// T *last()  const { return &impl_.v_[impl_.vused_]; }
// Returns pointer to one past the last constructed block.
// variant:     T *last()  const { return impl_.v_; }
        Helpers::construct( last(), data );
        ++impl_.vused_;
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

酒中人 2024-10-04 01:35:01
// ...
if( p != last() )
{
    this->push_back(data);

此行之后,pPos 可能不再是有效的指针。

然后,控制台将不再关闭,直到我关闭 VSC++08 本身。

尝试单击调试器中的“停止”按钮?

// ...
if( p != last() )
{
    this->push_back(data);

After this line pPos may not be a valid pointer anymore.

The console then proceeds to never shutdown again until I close VSC++08 itself.

Tried clicking the Stop button in the debugger?

离鸿 2024-10-04 01:35:01

从调试器和 ybungalobil 中,可以看到 pPos 在

if( p != last()
{
    this->push_back(data);

代码部分的特殊情况后无效。如果调整数组大小,则指针将失效。为了解决这个问题,我只是在推送之前存储了 const T pos = *pPos ,因此在推送之后删除了 *pPos 指针的使用。

更新的代码:

const T pos = *pPos;
T *p = ( std::find( this->first(), this->last(), pos ) );
if( p != last() )
{
    this->push_back(data);
    p = ( std::find( this->first(), this->last(), pos ) );

    T *right = (this->last() - 1);
    T *left  = (this->last() - 2);
    while( *p != data )
        std::iter_swap( left--, right-- ); 

    return const_cast<T*>(p);
}

From the Debugger, and from ybungalobill, it is possible to see that pPos is invalidated after a special case in the

if( p != last()
{
    this->push_back(data);

part of the code. If the array is resized, the pointer is invalidated. To bridge this, I simply stored const T pos = *pPos before the push and therefore removed the use of the *pPos pointer after a push.

Updated code:

const T pos = *pPos;
T *p = ( std::find( this->first(), this->last(), pos ) );
if( p != last() )
{
    this->push_back(data);
    p = ( std::find( this->first(), this->last(), pos ) );

    T *right = (this->last() - 1);
    T *left  = (this->last() - 2);
    while( *p != data )
        std::iter_swap( left--, right-- ); 

    return const_cast<T*>(p);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文