C ++ (Windows) Почему векторы ошибаются в памяти? - PullRequest
0 голосов
/ 09 августа 2011

Хорошо, возможно я делаю что-то смертельно глупое, но я злюсь.Весь день я имел дело с векторами, хранящими в них указатели на мой собственный класс, но они запутались (большую часть времени).Иногда, когда я прохожу их, я получаю переменную другого вектора, иногда я получаю полную бессмыслицу из памяти.

Вот код:

vector<TCPClientProtocol*> clients;
vector<TCPClientProtocol*> robots;

//this function gets names from "robots" and sends them to all the "clients"
void sendRobotListToClients(){
    //collect the list:
    int numRobots = robots.size();
    char *list = (char*)malloc(numRobots * USERNAME_SIZE);
    for(int i=0; i<numRobots; i++){
        int namelen = strlen(robots[i]->name);
        memcpy(&list[i*USERNAME_SIZE], robots[i]->name,
            namelen);
        if(namelen < USERNAME_SIZE)
            list[i*USERNAME_SIZE + namelen] = (char)0;
    }

    //send it to all clients:
    int numClients = clients.size();
    for(int i=0; i<numClients; i++){
        int result = clients[i]->sendRobotList(list, numRobots);
        if(result < 0){
            cout<<"Failed sending refreshed list to "
                <<clients[i]->name<<"."<<endl;
        }
    }

    delete list; //forgot to add this before
}

//How I created vectors:
vector<TCPClientProtocol*> clients;
vector<TCPClientProtocol*> robots;

//and this is how I add to them:
robots.push_back(robot);

По сути, яЯ не получаю память, которую я хочу.Я собираюсь использовать массивы или создать свой собственный класс, но я хотел динамическое хранение.Это глупо, хотя ...

robots.push_back(robot1);
clients.push_back(client1);

Например:

TCPClientProtocol *robot = new TCPClientProtocol(mySocket); //create with existing socket
robot->name = "robot1";
cout<<robot->name<<endl; //prints correctly
robots.push_back(robot);
... //do some other stuff (this IS multithreaded, mind you)
cout<<robots[0]->name<<endl; //prints something strange

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

robot->sendData(buffer, lenght);
robot->receiveData(buffer, length);

и т. Д.После этого я снова попытаюсь сослаться на них.Я не могу поместить весь код здесь ... это более 500 строк.

Затем я собираю имена роботов, и я либо получаю бред, либо имя клиента.В любом случае, спасибо за вашу помощь.

РЕДАКТИРОВАТЬ: Я специально проверил его, чтобы точно увидеть, что он делал на каждом шагу.Он распечатал точное имя / строку (робот-> имя), который я хотел.Однако после того, как он был вставлен в вектор, я взял тот же самый указатель из вектора, он больше не указывал на правильное имя, а дал мне что-то совершенно другое.Вот почему я в замешательстве.Мои явно плохие манипуляции с памятью работают достаточно хорошо, когда векторы не задействованы.

Функция, которая добавляет непосредственно к вектору:

void addRobotToList(TCPClientProtocol *robot){
    //add robot to list
    robots.push_back(robot);
    cout<<"Added "<<robot->name<<endl;
}

Функция, которая вызывает эту функцию (предупреждение: долго!) - и да, я имею в виду разделить это, но это своего рода черновик:

DWORD WINAPI AcceptThread(void* parameter){
TCPClientProtocol* cl = (TCPClientProtocol*)parameter;

TCPHeader *head = new TCPHeader;
loginInfo *logInfo = new loginInfo;

//Read header.
int result = cl->receiveHeader(head);
if(result < 0)
    return -1;
//Check data. Expected: DATATYPE_CONNETION_REQUEST
//  and check protocol version.
if( head->version != (char)PROTOCOL_VERSION ||
    head->type != (char)DATATYPE_CONNECTION_REQUEST ||
    head->size != (int)CONNECTION_REQUEST_LENGTH){
        goto REJECT;
}

cout<<"Accepted connection."<<endl;

result = cl->requestLoginInfo();
if(result < 0)
    goto CONNECTIONLOST;

//Read header.
result = cl->receiveHeader(head);
if(result < 0)
    goto CONNECTIONLOST;
if(head->type != DATATYPE_LOGIN_INFO){
    goto REJECT;
}

//read login information
result = cl->receiveLoginInfo(logInfo);
if(result < 0)
    goto CONNECTIONLOST;

//check for authentication of connector. If failed, return.
if(!authenticate(logInfo)){
    goto REJECT;
}

cout<<"Authenticated."<<endl;

//add name to robot/client
cl->name = logInfo->username;

//Check for appropriate userType and add it as a variable:
switch(logInfo->userType){
case USERTYPE_ROBOT:
    cl->userType = USERTYPE_ROBOT;
    cl->isClient = false;
    cout<<"Robot connected: "<<cl->name<<endl;
    break;
case USERTYPE_CLIENT:
    cl->userType = USERTYPE_CLIENT;
    cl->isClient = true;
    cout<<"Client connected: "<<cl->name<<endl;
    break;
default:
    goto REJECT;
    break;
}

//Send a phase change to PHASE 2:
result = cl->notifyPhaseChange(2);
if(result < 0)
    goto CONNECTIONLOST;

//if client, send robot availability list and listen for errors
//  and disconnects while updating client with refreshed lists.
if(cl->isClient){
    //add client to clients list:
    clients.push_back(cl);

    //send initial list:
    int numRobots = robots.size();
    char *list = (char*)malloc(numRobots * USERNAME_SIZE);
    for(int i=0; i<numRobots; i++){
        cout<<(i+1)<<" of "<<numRobots<<": "<<robots[i]->name<<endl;
        int namelen = strlen(robots[i]->name);
        memcpy(&list[i*USERNAME_SIZE], robots[i]->name,
            namelen);
        if(namelen < USERNAME_SIZE)
            list[i*USERNAME_SIZE + namelen] = (char)0;
    }
    result = cl->sendRobotList(list, numRobots);
    if(result < 0){
        removeClientFromList(cl->name);
        goto CONNECTIONLOST;
    }

    cout<<"Sent first robot list."<<endl;

    //wait to receive a ROBOT_SELECTION, or error or disconnect:
    result = cl->receiveHeader(head);
    if(result < 0){
        removeClientFromList(cl->name);
        goto CONNECTIONLOST;
    }
    if(head->type != DATATYPE_ROBOT_SELECTION){
        removeClientFromList(cl->name);
        goto REJECT;
    }

    //receive and process robot selection
    char *robotID = (char*)malloc(ROBOT_SELECTION_LENGTH+1);
    result = cl->receiveRobotSelection(robotID);
    robotID[USERNAME_SIZE] = (char)0;
    robotID = formatUsername(robotID);
    if(result < 0){
        removeClientFromList(cl->name);
        goto CONNECTIONLOST;
    }

    cout<<"Got a selection.."<<endl;

    //get the robot and remove it from list
    TCPClientProtocol *robot = removeRobotFromList(formatUsername(robotID));
    cout<<"Removal win."<<endl;
    //check robot status:
    if(robot == NULL){
        //TRY AGAIN
        cout<<"Oh mai gawsh, robot is NULL!"<<endl;
        getch();
    }
    else if(!robot->tcpConnected()){
        //TRY AGAIN
        cout<<"Oh mai gawsh, robot DISCONNECTED!"<<endl;
        getch();
    }else{
        cout<<"Collected chosen robot: "<<robot->name<<endl;
    }

    //request stream socket information from client
    result = cl->requestStreamSocketInfo();
    if(result < 0){
        removeClientFromList(cl->name);
        addRobotToList(robot); //re-add the robot to availability
        goto CONNECTIONLOST;
    }

    result = cl->receiveHeader(head);
    if(result < 0){
        removeClientFromList(cl->name);
        addRobotToList(robot); //re-add the robot to availability
        goto CONNECTIONLOST;
    }

    //check for datatype
    if(head->type != DATATYPE_STREAM_SOCKET_INFO){
        removeClientFromList(cl->name);
        addRobotToList(robot); //re-add the robot to availability
        goto REJECT;
    }
    //receive stream socket info:
    char *ip = (char*)malloc(20);
    int port;
    result = cl->receiveStreamSocketInfo(ip, &port);
    if(result < 0){
        removeClientFromList(cl->name);
        addRobotToList(robot); //re-add the robot to availability
        goto CONNECTIONLOST;
    }

    cout<<"Got ip: "<<ip<<" port: "<<port<<endl;

    //send stream socket information to robot
    result = robot->sendStreamSocketInfo(ip, port);
    if(result < 0){
        //RETURN CLIENT TO 'step 5'
        removeClientFromList(cl->name);
        delete robot;
        goto CONNECTIONLOST;
    }

    //send phase changes to both, and use this thread
    //  to monitor signals from client to robot.
    result = cl->notifyPhaseChange(3);
    if(result < 0){
        addRobotToList(robot); //re-add the robot to availability
        removeClientFromList(cl->name);
        goto CONNECTIONLOST;
    }
    result = robot->notifyPhaseChange(3);
    if(result < 0){
        //RETURN CLIENT TO 'step 5'
        removeClientFromList(cl->name);
        delete robot;
        goto CONNECTIONLOST;
    }

    cout<<"PHASE 3 INITIATED"<<endl;
    removeClientFromList(cl->name);

    //run a thread sending connections from CLIENT to ROBOT.
    while(true){
        cout<<"Listening for header..."<<endl;
        //read next header from client
        result = cl->receiveHeader(head);
        cout<<"Got something"<<endl;
        if(result < 0){
            cout<<"Failed read."<<endl;
            delete robot;
            goto CONNECTIONLOST;
        }
        if(head->type != DATATYPE_COMMAND){
            cout<<"Not a command. Protocol mismatch"<<endl;
            continue;
        }

        cout<<"Gots header"<<endl;

        //read command
        result = cl->receiveCommand();
        if(result < 0){
            //RESET ROBOT!
            delete robot;
            goto CONNECTIONLOST;
        }

        cout<<"Got data."<<endl;

        result = robot->sendCommand((char)result);
        if(result < 0){
            //RESET CLIENT!
            delete robot;
            goto CONNECTIONLOST;
        }
    }

    //spawn a thread for robot-to-client and client-to-robot comm,
    //  possibly just client-to-robot.
    //send a phase change (to phase 3) - in thread!
}

//if robot, add to robot list and wait.
else{
    //add robot to robots list:
    addRobotToList(cl);
}

delete head;
delete logInfo;
return 0;

//Clean up variables and send reject message
REJECT:
cout<<"Connection rejected."<<endl;
cl->sendRejection();
delete cl;
delete head;
delete logInfo;
return -1;

CONNECTIONLOST:
cout<<"Connection lost."<<endl;
delete cl;
delete head;
delete logInfo;
return -1;
}

Ответы [ 3 ]

5 голосов
/ 09 августа 2011

Ваш код - ужасное сочетание C и C ++, и, конечно, все ошибки в C-частях :).Так что не вините векторы, а вместо этого посмотрите на все эти ужасные манипуляции с памятью низкого уровня.

Мне кажется, что

  • Нет никакой гарантии, что вы не переполните границы списка
  • Нет никакой гарантии, что список будет завершен нулем
  • Утечка памяти в списке

Лучше всего начать с использования std :: string.

3 голосов
/ 09 августа 2011

Некоторые вещи, на которые стоит обратить внимание при взгляде на код, будут следующие:

  • Вы должны использовать new вместо malloc.
  • Вы должны использовать умные указатели , не сохраняя необработанные указатели в вашем векторе.
  • Используйте iterators для перебора содержимого вектора.
  • Используйте std::string, а не char*
1 голос
/ 10 августа 2011

решено: ошибка при удалении структуры loginInfo привела к удалению объекта name внутри него, что сделало недействительными указатели в переменной name. Спасибо всем, кто порекомендовал использовать строки, они определенно решили проблему и не так опасны в использовании.

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