首页 > 解决方案 > 中止(核心转储)从 C++ 中的向量中删除元素

问题描述

我正在尝试使用“MUD 游戏编程”学习 C++,并且正在研究示例,但是当我尝试从向量中删除连接时,我收到错误消息:“已中止(核心转储)。” 这通常在从向量中擦除最后一个时发生。我尝试了一些解决方案,例如在删除之前检查迭代器 != connlist.end() ,如果列表中只剩下一个元素,则尝试使用 connlist.clear() ,但这些似乎没有帮助.

我正在使用 g++ 编译和使用 Cygwin。

错误发生在 connlist.erase() 中,您可以看到当用户键入“quit”时会调用它。

void NMudServer::StartListening() {

    fd_set read_set;
    std::vector<SocketLib::Connection>::iterator itr;       
    std::vector<SocketLib::Connection>::iterator itr2;  
    TIMEVAL zerotime;
    zerotime.tv_usec = 0;
    zerotime.tv_sec = 0;
    char buffer[buf_len];
    int err;
    lsock.Listen( default_port );

    if( lsock.IsListening() ) {
        std::cout << "Telnet listening on port " << default_port << "." << std::endl;
    } else {
        std::cout << "Could not start Telnet listening socket! - Last error code: " << WSAGetLastError() << std::endl;
        return;
    }

    while( lsock.IsListening() ) {
        FD_ZERO( &read_set );
        FD_SET( lsock.GetSock(), &read_set );

        for( itr = connlist.begin(); itr != connlist.end(); ++itr ) {
            FD_SET( itr->GetSock(), &read_set );
        }
    
        int sel = select( 0x7FFFFFFF, &read_set, NULL, NULL, &zerotime );           

        if( sel > 0 ) {

            if( FD_ISSET( lsock.GetSock(), &read_set ) ) {
                SocketLib::DataSocket dsock = lsock.Accept();
                SocketLib::Connection conn( dsock, buf_len );           
                connlist.push_back( conn );
                conn.Send( "Hello!\r\n", 8 );
            }           

            for( itr = connlist.begin(); itr != connlist.end(); ++itr ) {

                if( FD_ISSET( itr->GetSock(), &read_set ) ) {

                    err = itr->Receive();

                    if( err == -1 ) {
                        std::cout << "Socket receiving error!" << std::endl;
                        std::cout << "Error code: " << WSAGetLastError() << std::endl;
                        std::cout << "Exiting due to error." << std::endl;
                        CloseAllConnections(); // This is when the connection is closed. Need to only close the one connection.
                        break;
                    } else if( err == 0 ) {
                        itr->Close();
                        connlist.erase( itr );
                        --itr;
                    } else if( itr->IsReady() ) {
                        int size;
                        size = itr->GetData( buffer );

                        if( strcmp( buffer, "servquit\r\n" ) == 0 ) {
                            CloseAllConnections();
                        } else if( strcmp( buffer, "quit\r\n" ) == 0 ) {
                            itr->Close();
                            connlist.erase( itr ); // When you go to erase the last element, it errors
                            --itr;
                        } else {
                            // Echo back the data to all connections
                            for( itr2 = connlist.begin(); itr2 != connlist.end(); ++itr2 ) {                                        

                                if( itr2->GetSock() != itr->GetSock() ) {

                                    int err2;
                                    itr2->Send( buffer, size );

                                    if( err2 == -1 ) {
                                        std::cout << "Socket sending error: " << WSAGetLastError() << std::endl;
                                    }
                                } else {
                                    itr2->Send( "\r\n", 2 );
                                }

                            } // end for
                        }// end if-else

                        itr->Reset();

                    } // end if-else-else

                } // end if
            } // end for
        } // end if sel
    } // end while
}

void NMudServer::CloseAllConnections() {
    lsock.Close();

    std::vector<SocketLib::Connection>::iterator itr;
    for( itr = connlist.begin(); itr != connlist.end(); ++itr ) {
        itr->Close();
    }

    WSACleanup();
}

作为参考,这本书是:Penton, Ron。MUD 游戏编程。美国马萨诸塞州波士顿:课程技术/Cengage Learning,2003 年。

标签: c++vectorberkeley-sockets

解决方案


您正在迭代一个向量并在循环内从中删除元素。这绝不是一个好主意。vector::erase使您随后用于进一步迭代的迭代器无效。

请参阅:https ://en.cppreference.com/w/cpp/container/vector/erase

这样做的原因是当你擦除一个元素时,一个向量可能会重新分配和/或移动元素。

可能的解决方案:

只需关闭该循环内的这些连接,然后执行擦除删除:

connlist.erase(std::remove_if(connlist.begin(), connlist.end(), is_closed), connlist.end());

类似于is_closed

is_closed = [](const SocketLib::Connection& c) {return !c.open();};

PS:我不知道,SocketLib所以你可能会改变is_closed一点

PPS:使用基于范围的循环,例如for(auto& connection : connlist) {...}

编辑:

正如评论中提到的,在这种情况下向量不会重新分配,其他点仍然存在。


推荐阅读