按属性搜索对象向量并返回迭代器

时间:2017-11-02 16:10:38

标签: c++ string vector lambda iterator

我有一个向量UserAccounts,其中包含自定义类类型的成员User:

class User{

public:

//CONSTRUCTORS 

User(){}

User(std::string username, std::string password)    
    //more efficient than using assignment operators
    : username(username), password(password), GBPbal(0), sharesbal(0){}

//DESTRUCTOR    
~User(){}

//SETTERS
void changeusername(std::string newuser){username = newuser;}
void changepassword(std::string newpassword){password = newpassword;}
void setGBP(float GBP){GBPbal = GBP;}
void setshares(int shares){sharesbal = shares;}

//GETTERS (HOLDINGS)
float getGBPbal(){return GBPbal;}
int getsharesbal(){return sharesbal;}
std::string getusername(){return username;}

//UPDATE HOLDINGS
float GBPUpdate(float adjustment){
GBPbal += adjustment;
return GBPbal;
}
int sharesUpdate(float adjustment){
sharesbal += adjustment;
return sharesbal;
}


protected:

private:



//Credentials
std::string username;
std::string password;

//Balances
float GBPbal;
int sharesbal;

friend class boost::serialization::access; 
template <typename Archive> 
void serialize(Archive &ar, const unsigned int version)
 {
ar & username;
ar & password;
ar & GBPbal;
ar & sharesbal;
}
};

我在main(类外)声明了一个字符串'username',该字符串已经被分配了一个已读入的值。

我的目标是在向量中搜索具有上述用户名==上述用户名的特定用户对象,并返回对它的引用,以便能够打印其GBPbal,sharebal并更新这些值。根据我的研究,答案如下:

        auto it = std::find_if(UserAccounts.begin(), 
UserAccounts.end(), [&username](User& obj)->bool {return 
obj.getusername() == username;});
    //return their user account  
    std::cout << it->getGBPbal();

但这似乎在运行时不起作用(cout不打印GBP bal)。我也不太了解'obj'部分,但我认为这是因为向量内部的实例/对象尚未明确命名。

提供一个最小的例子很难,因为一切都是相互关联的。

//INTERNET DOMAIN SERVER: TCP.
//SERVER SIDE
//argv[1] = Port number for listening.
//argv[2] = Specify the maximum number of threads manually (optional).

// C++/STL/UNIX HEADERS
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>             //Datatypes used for sys calls (and used in <sys/socket.h> and <sys/socket.h>).
#include <sys/socket.h>            //Definitions of structures used for sockets.
#include <netinet/in.h>            //Contains constants and structures needed for internet domain addresses
#include <thread>
#include <vector>
#include <map>
#include <string>
#include <iostream>
#include <cstdlib>
#include <mutex>


//BOOST HEADERS
#include <boost/archive/text_iarchive.hpp>        //Input archive.
#include <boost/archive/text_oarchive.hpp>        //Output archive.
#include <boost/serialization/map.hpp>            //Allows for archiving maps specifically.
#include <boost/serialization/string.hpp>        //Allows for archiving strings specifically.
#include <boost/serialization/vector.hpp>        //Allows for archiving vectors specifically.
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>            //Filestream for archiving

//MY HEADERS
#include "userclass.h"

//PP DIRECTIVES
void clientHandler(int sock, std::map<std::string, std::string>& userandPass, std::vector<class User>& UserAccounts);
void error(const char *msg);
void userandPassBackup(std::map<std::string, std::string>& userandPass);
void userandPassLoad(std::map<std::string, std::string>& userandPass);
template <typename SaveClass>
void saveData(const std::string filename, const SaveClass &c);
template <typename LoadClass>
void loadData(const std::string filename, LoadClass &c);//was having issues with const as second argument


//THREADING SETTINGS
unsigned int MAX_THREADS = std::thread::hardware_concurrency(); // MAX THREADS: LET COMPILER DECIDE BASED ON MACHINE RUNNING SERVER
// const unsigned int  MAX_THREADS = atoi(argv[2]);                      // OR YOU CHOOSE
unsigned int CURR_THREADS = 1;        


//LOCKS
std::mutex UserAccountsM;
std::mutex userandPassM;


                                  // CURRENT NUMBER OF THREADS

int main(int argc, char *argv[])
{    
// USER DATA
std::map<std::string, std::string> userandPass;                                      //BANK OF CREDENTIALS
std::vector<class User> UserAccounts;                                            //User account details 

//userandPassLoad(std::ref(userandPass));        
loadData< std::map<std::string, std::string> >("userandPassBackup.txt", userandPass);    //LOAD IN DATA FROM BACKUP FILE
loadData<std::vector<User>>("accounts.txt", UserAccounts);


                        //These two variables store the values returned by the socket system call and the accept system call.    
     int sockfd;            // sockfd and newsockfd are file descriptors  ...
     int newsockfd;            // === array subscripts into the file descriptor table (which store pointers to iostreams).
     unsigned int portno;            // Portno stores the port that the server will allow connections to.
     socklen_t clilen;        //stores client address (needed for accept system call)

//     char buffer[256];        // Socket is connected to this buffer. Server reads characters from here.
     struct sockaddr_in serv_addr, cli_addr; // Structures containing internet addresses (client and server addresses).
//     int n;                    // return value for read/write calls - ie how many characters have been read/written
     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided\n");
         exit(1);
     }

     // CREATE NEW SOCKET
     //    THREE ARGUMENTS:#1 . ADDRESS DOMAIN OF SOCKET - INTERNET DOMAIN FOR US, AF_UNIX IS AN ALTERNATIVE.
     //                 #2 . TYPE OF SOCKET - EITHER STREAM (CONTINUOUS) OR DATAGRAM (CHUNK BY CHUNK).
     //                    #3 . PROTOCOL: TCP OR UDP. LEAVE 0 AND THE OS WILL DECIDE WHAT IS APPROPRIATE.
     // SOCKET CALL WILL RETURN AN ENTRY (SMALL INT > 0) TO THE FILE DESCRIPTOR TABLE. 
     // VALUE IS USED BY ALL REFERENCES TO THE SOCKET HEREON.
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd < 0) 
        error("ERROR opening socket");

     // SETS ALL BUFFER VALUES TO 0. ARGS ARE POINTER TO BUFFER, BUFFER SIZE. 
     bzero((char *) &serv_addr, sizeof(serv_addr));

     // CONVERTS PORT NUMBER DEFINED BY USER FROM A STRING OF NUMBERS TO AN INTEGER
     portno = atoi(argv[1]);

     // SERV ADDR IS OF TYPE SOCKADDR_IN WHICH HAS FOUR FIELDS
     // CODE FOR SOCK FAMILY: ALWAYS SET TO AF_INET
     serv_addr.sin_family = AF_INET;
     //SETS IP OF SERVER. INADDR_ANY FETCHES THIS - ITS PRESET.
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     // PORT NO: MUST BE CONVERTED FROM HOST BYTE ORDER TO NETWORK BYTE ORDER USING HTONS
     serv_addr.sin_port = htons(portno);

     //BINDS SOCKET (OUR PORT) TO ADDRESS - ARGUMENTS ARE SOCKET FILE DESCRIPTOR, POINTER TO STRUCTURE OF TYPE SOCKADDR
     if (bind(sockfd, (struct sockaddr *) &serv_addr,
              sizeof(serv_addr)) < 0) 
              error("ERROR on binding");

    // LISTEN FOR CONNECTIONS. ARGS: SOCKET FD, SIZE OF BACKLOG QUEUE - IE HOW MANY CONNECTIONS CAN WAIT IN LINE TO BE PROCESSED
     listen(sockfd,5);
     clilen = sizeof(cli_addr);

    //vector of client threads
     std::vector<std::thread> clientThreads;  

     // MULTITHREADING FOR SIMULTANEOUS CONNECTIONS
   //  extern const int maxthreads = atoi(argv[2]);     //User defined maximum number of threads for server
     // PUT ACCEPT STATMENT INSIDE INF WHILE LOOP SO SERVER RUNS INDEFINITELY.
      while (1) {

           searchAgain:
    newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);

           if (newsockfd < 0){
                 error("ERROR on accept");
            }
//         pid = fork();
//         if (pid < 0)
//             error("ERROR on fork");
           if(CURR_THREADS < MAX_THREADS){
//             close(sockfd); /* this was big problem before: we kept getting error 'bad file descriptor' on client side.
             clientThreads.push_back(std::thread(clientHandler, newsockfd, std::ref(userandPass), std::ref(UserAccounts)));

            goto searchAgain;
             for(auto &t : clientThreads){
             t.join();
             }
           exit(0);
         }
         else close(newsockfd);
     } //end of loop
     close(sockfd);
     return 0; 
}

void clientHandler(int sock,std::map<std::string, std::string>& userandPass, std::vector<class User>& UserAccounts)
{
   int n;
   char buffer[256];

   // Welcome Messages
   bzero(buffer,256);
   write(sock, "Welcome to Chris's exchange! Write 'exit' to exit at any time.",255); // NB third argument always this value to prevent overlap.
   bzero(buffer, 256);
   loginmenu :
   write(sock, "Do you wish to login or register a new account?",255);
   bzero(buffer, 256);


   while (1) {
   n = read(sock,buffer,255); // NB blocks until client executes a write. Initially reads login/register request
//   printf("Client message: %s",buffer); //client message

   if (n < 0) error("ERROR reading from socket");   
   if(strcmp(buffer, "login\n") == 0){ // \n is extremely important!
        bzero(buffer, 256);
           n = write(sock,"Please enter your username.",255);
           bzero(buffer, 256);
           // read their username and password
           n = read(sock,buffer,255);
           std::string username(buffer);
           bzero(buffer, 256);
           n = write(sock,"Please enter your password.",255);
           bzero(buffer, 256);
           n = read(sock,buffer,255);
           std::string password(buffer);
           bzero(buffer, 256);
           if(userandPass.find(username)->second == password){
           write(sock, "Login successful. Press return to see balances.\n", 255);
           auto it = std::find_if(UserAccounts.begin(), UserAccounts.end(), [&username](User& obj)->bool {return obj.getusername() == username;});
           //return their user account  
           std::cout << it->getGBPbal();
           bzero(buffer, 256);
          read(sock,buffer,255);
           std::string rtn(buffer);
           if (rtn == "\n"){
           bzero(buffer, 256);
           goto loginmenu;
                   }         
           } 
          } 
   else if(strcmp(buffer, "register\n") == 0){
         bzero(buffer, 256);
           n = write(sock,"Please enter your desired username",255);
           // Write code for reading these into strings
           bzero(buffer, 256);
           n = read(sock,buffer,255);
           std::string username(buffer);
           bzero(buffer, 256);
           write(sock, "Please enter your desired password", 255); 
           bzero(buffer, 256);        
           n = read(sock,buffer,255);
           std::string password(buffer);
           bzero(buffer, 256);
           //If it exists already try again
 //      std::unique_lock<std::mutex> UPlock(userandPassM); // to be exception safe
           if (userandPass.find(username) != userandPass.end()){
           write(sock, "That username is taken. Please try again.", 255);
           }
           // If it doesn't exist, happy days - lets add it to the database.
            else if (userandPass.find(username) == userandPass.end()){
           userandPass.emplace(username, password);
 //     std::unique_lock<std::mutex> UAlock(UserAccountsM);
           UserAccounts.emplace_back(username, password);
           //userandPassBackup(userandPass);
           saveData< std::map<std::string, std::string> >("userandPassBackup.txt", userandPass); // have removed &. why is this needed. i want to pass by reference
 //     UPlock.unlock();
           saveData< std::vector<User> >("accounts.txt", UserAccounts); // have removed &. why is this needed. i want to pass by reference
//    UAlock.unlock();
           while(1){
           write(sock, "Account created successfully. Press return to continue", 255);
           bzero(buffer, 256);
           read(sock,buffer,255);
           std::string rtn(buffer);
           if (rtn == "\n"){
           bzero(buffer, 256);
           goto loginmenu;
                   }
                   }
          }
       }
   else{
   n = write(sock,"Invalid request, please try again. ",255);
    bzero(buffer, 256);
    }

   if (n < 0) error("ERROR writing to socket");
   }
   --CURR_THREADS;
}

//ERROR HANDLING
void error(const char *msg)        // System call error failures: Errors displayed on stderr.
{
    perror(msg);
    exit(1);
}

template <typename SaveClass>
void saveData(const std::string filename, const SaveClass& c)
{
    // File to be written to
    boost::filesystem::remove(boost::filesystem::current_path() / filename);
    boost::filesystem::path myFile = boost::filesystem::current_path() / filename;
    // Declare an output file stream ofs that writes serialises to our backup file.
    boost::filesystem::ofstream ofs(myFile);
    // Declare archive ta that will use our output file stream
    boost::archive::text_oarchive ta(ofs);
    // Write to file
    ta << c;
    // How many records have been archived?
    std::cout << c.size() << " records from have been successfully backed up to " << myFile << "\n";
}

template <typename LoadClass>
void loadData(const std::string filename, LoadClass& c)
{
    boost::filesystem::path myFile = boost::filesystem::current_path() / filename;
    boost::filesystem::ifstream ifs(myFile);
    boost::archive::text_iarchive ta(ifs);
    // Write to file
    ta >> c;
    std::cout << c.size() << " records from have been successfully loaded from " << myFile << "\n";
}

注意:我尝试过这样的电话:

        std::cout << UserAccounts[1].getusername();

在'auto it line'之后,它返回该对象的正确用户名。

1 个答案:

答案 0 :(得分:0)

修复了问题。我将类中的所有getter方法都改为常量方法,并显式声明了迭代器&#39; it&#39;是类型std :: vector :: iterator它。它现在可以很好地检索信息。

    std::vector<User>::iterator it;
    it = std::find_if(UserAccounts.begin(), UserAccounts.end(),[username](User const& obj)->bool{return obj.getusername() == username;});
    if(it != UserAccounts.end()){
    std::cout<<"Name = "<<it->getGBPbal() << std::endl;
    } else{
    std::cout<<"Item not Found"<<std::endl;
    }