访问成员时生成超出范围错误的结构向量 C++

Vector of structs generating out-of-range error when member is accessed C++

我正在编写图书馆应用程序。应用程序的一部分允许用户登录。我正在使用结构向量来存储 usernames/passwords。当我尝试访问结构的成员变量时,出现超出范围错误。向量已满(我检查了 vector.size 和 vector.empty 方法),我相信我正确地为成员变量赋值(尽管显然我不是)。

这里是main.cpp:

#include <iostream>
#include <string>
#include <vector>
#include <fstream>

#include "userService.h"

using namespace std;

void countRecords(string& pathName, ifstream& inFile);
void loadCredentials(string pathName, ifstream& inFile, string& username, string& password, userService newUser);
void login(string username, string password, userService newUser, bool& loggedIn);

int numRecords;

int main()
{
    string username, password;
    string pathName = "/home/seth/Desktop/credentials";
    ifstream inFile;
    userService newUser;
    char menuSelection;
    bool loggedIn = false;

    countRecords(pathName, inFile);

    cout << "Welcome to Library Information System." << endl << endl;

    do{
        cout << "choose a) to login or b) to register as a new user." << endl << endl;
        cin >> menuSelection;
        switch (menuSelection)
        {
        case 'a':
        {
            cout << "Username: ";
            cin >> username;
            cout << endl;
            cout << "Password: ";
            cin >> password;
            cout << endl;
            loadCredentials(pathName, inFile, username, password, newUser);
            login(username, password, newUser, loggedIn);
            if (loggedIn == true)
            {
                cout << "You logged in! " << endl; //placeholder, will be more menu options here
            }
            else
            {
                cout << "Invalid credentials! Please check your username and password and try again!" << endl;
            }
            break;
        }
        }
    } while (loggedIn == false);


    return 0;
}
void countRecords(string& pathName, ifstream& inFile)
{
    string temp; //string to count records using getline

    inFile.open(pathName);

    while (inFile)
    {
        getline(inFile, temp, '\n');
        if (inFile.eof())
        {
            break;
        }
        ++numRecords;
    }
    cout << "numRecords = " << numRecords << endl;
    inFile.close();
    inFile.clear(std::ios_base::goodbit);
}

void loadCredentials(string pathName, ifstream& inFile, string& username, string& password, userService newUser)
{
    string tempUsername, tempPassword;

    inFile.open(pathName);

    if (!inFile)
    {
        cout << "Error opening file" << endl;
    }

    for (int i = 0; i < numRecords; i++)
    {
        getline(inFile, tempUsername, ',');
        getline(inFile, tempPassword, '\n');
        newUser.loadCredentials(tempUsername, tempPassword);
    }

}

void login(string username, string password, userService newUser, bool& loggedIn)
{

    newUser.resetVectorCounter();

    for (size_t i = 0; i < numRecords; i++)
    {
        cout << "i = " << i << endl;
        cout << newUser.getUsername() << endl;
        cout << newUser.getPassword() << endl;
        newUser.incrementVector();
    }   
}

userService.h:

#include <string>
#include <fstream>
#include <vector>

using namespace std;

struct credentials
    {
        string username = "";
        string password = "";

    };

class userService
{

private:

    vector<credentials> credentialsList;
    int vectorCounter = 0;
    string username_, password_;

public:

    void loadCredentials(string username_, string password_);
    bool check();
    int sizeOfVec();
    string getUsername();
    string getPassword();
    void incrementVector();
    void resetVectorCounter();
};

用户服务的实现:

#include <iostream>
#include <string>
#include <fstream>
#include <vector>

#include "userService.h"

using namespace std;

credentials users;

void userService::loadCredentials(string username_, string password_)
{
    users.username = username_;
    users.password = password_;
    credentialsList.push_back(users);
}

bool userService::check()
{
    return credentialsList.empty();
}
int userService::sizeOfVec()
{
    return credentialsList.size();
}
string userService::getUsername()
{
    return credentialsList.at(vectorCounter).username;
}
string userService::getPassword()
{
    return credentialsList.at(vectorCounter).password;
}
void userService::incrementVector()
{
    vectorCounter++;
}

void userService::resetVectorCounter()
{
    vectorCounter = 0;
}

抛出的确切错误是:

'std::out_of_range' what(): vector::_M_range_check: __n (即0) >= this->size (即0)

这在调用 getUserName 后立即发生。我相信这意味着成员变量是空的,但如果是这样,我不知道如何正确地给它们赋值。任何帮助将不胜感激。

我试过使用调试器,这里是调试器显示此问题的地方:

protected:
  /// Safety check used only from at().
  void
  _M_range_check(size_type __n) const
  {
if (__n >= this->size())
  __throw_out_of_range_fmt(__N("vector::_M_range_check: __n "
                   "(which is %zu) >= this->size() "
                   "(which is %zu)"),
               __n, this->size());
  }

您的代码确实使用越界索引访问 std::vector

这是我机器上 gdb 的堆栈跟踪:

#0  0x00007ffff74aa428 in __GI_raise (sig=sig@entry=6)
    at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007ffff74ac02a in __GI_abort () at abort.c:89
#2  0x00007ffff7ae484d in __gnu_cxx::__verbose_terminate_handler() ()
   from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7ae26b6 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007ffff7ae2701 in std::terminate() ()
   from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007ffff7ae2919 in __cxa_throw ()
   from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x00007ffff7b0b3f7 in std::__throw_out_of_range_fmt(char const*, ...) ()
   from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#7  0x00000000004029b6 in std::vector<credentials, std::allocator<credentials> >::_M_range_check (this=0x7fffffffe320, __n=0)
    at /usr/include/c++/5/bits/stl_vector.h:803
#8  0x00000000004024cf in std::vector<credentials, std::allocator<credentials> >::at (this=0x7fffffffe320, __n=0) at /usr/include/c++/5/bits/stl_vector.h:824
#9  0x000000000040179d in userService::getUsername[abi:cxx11]() (
    this=0x7fffffffe320) at socc.cc:61
#10 0x0000000000402059 in login (username="a", password="aa", newUser=..., 
    loggedIn=@0x7fffffffe19f: false) at socc.cc:185
#11 0x0000000000401aa4 in main () at socc.cc:120

我的 "credentials" 文件有以下几行:

a,aa
b,bb
c,cc

因此,当我使用 "a" 作为用户名和 "aa" 作为密码时,应该可以登录成功。

但是,您有:

string userService::getUsername()
{
   return credentialsList.at(vectorCounter).username;
}

调用该函数时,credentialsList 为空,vectorCounter0。那不行。

这就解释了为什么会出现错误。然而,修复并不简单。

我认为您不清楚 "credentials" 文件中的数据需要如何存储以及如何使用它们来验证用户身份。

这里有几点值得思考。

  1. userService 可以提供对用户进行身份验证的能力。但是,它不是用户。因此,为什么将 username_password_ 作为 class.

  2. 的成员变量完全没有意义
  3. "credentials" 文件中的凭据列表是程序的全局数据。将它们存储在 userService 的成员变量中是没有意义的,除非您的程序可以保证只有 userService.

  4. 的实例
  5. vectorCounter作为userService的成员变量也毫无意义。当您需要遍历向量的元素时,有更好的选择。不仅如此,迭代的逻辑应该是函数局部的,其任何局部变量都应该是函数局部变量。

  6. userService的许多成员函数不适合class的职责。唯一对我有意义的成员函数是:

    void loadCredentials(string filename);
    bool authenticateUser(credentials const& c);
    

这是一个适合我的更新程序(除了你没有任何代码供用户选择 'b')。

#include <iostream>
#include <string>
#include <fstream>
#include <vector>

using namespace std;

struct credentials
{
   string username;
   string password;
};

class userService
{
   private:

      vector<credentials> credentialsList;

   public:

      void loadCredentials(string filename);
      bool authenticateUser(credentials const& c);
};

void userService::loadCredentials(string filename)
{
   string tempUsername, tempPassword;

   std::ifstream inFile(filename);

   if (!inFile)
   {
      cout << "Error opening file" << endl;
   }

   while (inFile )
   {
      if ( !getline(inFile, tempUsername, ',') )
      {
         return;
      }

      if ( !getline(inFile, tempPassword, '\n') )

      {
         return;
      }

      credentialsList.push_back(credentials{tempUsername, tempPassword});
   }
}

bool userService::authenticateUser(credentials const& c)
{
   for ( auto const& item : credentialsList )
   {
      if ( item.username == c.username &&
           item.password == c.password )
      {
         return true;
      }
   }
   return false;
}

int main()
{
   string username, password;
   // string pathName = "/home/seth/Desktop/credentials";
   string pathName = "credentials";

   // The only instance of userService used in the program.
   userService service;

   char menuSelection;
   bool loggedIn = false;

   // Load the credentials file once and then use for all subsequent checks.
   service.loadCredentials(pathName);

   cout << "Welcome to Library Information System." << endl << endl;

   do {
      cout << "choose a) to login or b) to register as a new user." << endl << endl;
      cin >> menuSelection;
      switch (menuSelection)
      {
         case 'a':
            {
               cout << "Username: ";
               cin >> username;
               cout << endl;
               cout << "Password: ";
               cin >> password;
               credentials c = {username, password};

               // The only instance of userService has all the credentials.
               // It can authenticate the user credentials.
               loggedIn = service.authenticateUser(c);
               if (loggedIn)
               {
                  cout << "You logged in! " << endl; //placeholder, will be more menu options here
               }
               else
               {
                  cout << "Invalid credentials! Please check your username and password and try again!" << endl;
               }
            }
            break;

         // Missing code for case 'b'
         // case 'b':
      }
   } while (loggedIn == false);

   return 0;
}