我有一个向量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'之后,它返回该对象的正确用户名。
答案 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;
}