2011-08-09 4 views
0

좋아, 내가 어쩌면 멍청한 짓을하고 있을지 모르지만, 나는 화가났다. 하루 종일 나는 내 자신의 클래스에 포인터를 저장하는 벡터를 다루었지만, 많은 시간을 낭비했다. 때로는 그들을 통과 할 때, 나는 다른 벡터의 변수를 얻게되고, 다른 때는 메모리에서 완전한 말도 안되는 것을 얻습니다.C++ (Windows) 벡터가 어떻게 메모리를 잘못 처리합니까?

여기에 코드의 일부입니다 : 기본적으로

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 라인이 넘습니다.

그런 다음 로봇 이름을 수집하고 나는 gibbrish 또는 고객의 이름을 얻습니다. 어쨌든, 도와 줘서 고마워.

편집 : 모든 단계에서 정확히 무엇을하고 있었는지 확인하기 위해 의도적으로 테스트했습니다. 그것은 내가 원하는 정확한 이름/문자열 (robot-> name)을 출력했다. 그러나 벡터에 푸시 된 후 벡터 내에서 정확히 동일한 포인터를 가져 왔고 더 이상 올바른 이름을 가리 키지 않고 대신 완전히 다른 것을 제공했습니다. 그래서 혼란 스럽네. 내 명백하게 나쁜 메모리 조작은 벡터가 관련되지 않을 때 충분히 잘 작동합니다.

벡터에 직접 추가 기능 :

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; 
} 
+1

포인터를 벡터에 저장하는 방법 - 코드 호소 샘플? – matekm

+2

데이터를 엉망으로 만들 수있는 곳을 명확히 할 수 있습니까? 'sendRobotList'에 있나요? – sergio

+3

벡터가 잘못 작동하는 것은 거의 없습니다. 그들이 어떻게 행동해야하는지, 그리고/또는 코드에서 실수를 저질렀다는 것을 오해 할 가능성이 더 큽니다. 이 경우, 실수는 벡터를 채우는 방법에 있습니다. –

답변

5

코드는 C와 C++의 끔찍한 조합이며 물론 모든 오류는 C 부분에 있습니다. :) 따라서 벡터를 비난하지 말고 그 끔찍한 저수준 메모리 조작을 살펴보십시오.

  • 당신이 목록의 경계를 오버 플로우 않는다는 보장이 없다는 것을 나에게 보이는리스트가 null이 목록 메모리
누수
  • 종료 될 것이라는 보장은 없습니다
  • std :: string을 사용하여 시작하는 것이 가장 좋은 조언 인 것처럼 보입니다.

  • +0

    또는 "ostringstream"이 "목록"을 생성합니다. – Nim

    +0

    문자열은 모두 null로 끝납니다. 나는 내 자신의 코드를 썼다. 문자열 대신 char *을 사용하는 이유는 모든 것을 보내기 위해 바이트/문자를 처리하는 프로토콜 클래스를 사용하여 소켓을 통해 모든 것을 전송하기 때문입니다. – Sefu

    +0

    @ 리차드 :'char'로 보내기 만하면'char'로 저장할 필요가 있습니다. –

    3

    것들 중 일부는 것입니다 여기에 코드를 하나 개의 표정으로 지적합니다 : 당신은 new 대신 malloc를 사용한다

    • .
    • 스마트 포인터 벡터에 원시 포인터를 저장하지 않아야합니다.
    • iterators을 사용하여 벡터 내용을 반복합니다.
    • 사용 std::string 및 해결되지 char*
    1

    는 : 오류 따라서 이름 변수의 포인터를 무효화 그 안에 이름 개체를 삭제에 LOGININFO 구조체 리드를 삭제했다. 문자열 사용을 권유 한 모든 사람들 덕분에 문제를 확실히 해결했으며 사용하기에 위험하지 않습니다.

    관련 문제