C ++ массив указателей возвращает одинаковое значение для каждого элемента - PullRequest
0 голосов
/ 02 февраля 2012

Я написал упрощенную версию кода, над которым я работаю, чтобы проиллюстрировать мою проблему. Я думаю, что что-то не так в main(), что заставляет метод showUsers() выводить одно и то же сочетание логина / пароля для каждого элемента (всегда последний добавленный).

Вот мой код:

#include <iostream>

using namespace std;

//=============================================================================
class AccountInfo {
private:
    char* _username;
    char* _password;
public:
    AccountInfo();
    AccountInfo(char* username, char* password);
    ~AccountInfo();
    void setUsername(char* username);
    void setPassword(char* password);
    char* getUsername();
    char* getPassword();
    friend ostream& operator<<(ostream& out, AccountInfo& x) {
        out << "Login: " << x.getUsername() << endl 
            << "Password: " << x.getPassword() << endl;
        return out;
    }
};

AccountInfo::AccountInfo() {
    _username = "";
    _password = "";
}

AccountInfo::AccountInfo(char* username, char* password) {
    _username = username;
    _password = password;
}

void AccountInfo::setUsername(char* username) {
    _username = username;
}

void AccountInfo::setPassword(char* password) {
    _password = password;
}

char* AccountInfo::getUsername() {
    return _username;
}

char* AccountInfo::getPassword() {
    return _password;
}

//=============================================================================
class UsersDB {
private:
    int _size;
    AccountInfo* _accounts[200];
public:
    UsersDB();
    ~UsersDB();
    int getSize();
    void addUser(AccountInfo* newUser);
    void showUsers();
};

UsersDB::UsersDB() {
    _size = 0;
}

UsersDB::~UsersDB() {
    delete[] _accounts;
}

int UsersDB::getSize() {
    return _size;
}

void UsersDB::addUser(AccountInfo* newUser) {
    _accounts[_size] = newUser;
    _size++;
}

void UsersDB::showUsers() {
    for (int i = 0; i < _size; i++) {
        cout << *_accounts[i] << endl;
    }
}

//---------------------------------------------------------emptyString function
void emptyString(char* token, int size) {
    for (int i=0; i < size; i++) token[i] = '\0';
}

//----------------------------------------------------------copyString function
void copyString (char* from, char* to, int size) {
    to = new char[size+1];
    for (int i=0; i < size; i++) to[i] = from[i];
    to[size] = '\0';
}

 //--------------------------------------------------------getNextToken function
int getNextToken(char* buffer, char* token, int startPos, 
                    int bufSize, int tokenSize, char delimeter) {
    int i, j;

    emptyString (token, tokenSize);

    i = startPos;
    j = 0;

    while ((buffer[i] == ' ') && (i < bufSize)) i++; //skipblanks
    if (i < 256) {
        while ((buffer[i] != delimeter) && (i < 256) && (j < tokenSize))
                token[j++] = buffer[i++];
    }
    return i;
}

//=============================================================================
int main() {
    char buffer[256];
    char userLoginName[9];
    char password[17];
    int i, j, k;
    char flag[3];;
    char command[11];
    char blank = ' ';

    UsersDB* users = new UsersDB();
    AccountInfo* tempAccount;

    while (!cin.eof()) { //while end of line is not reached
        cin.getline(buffer, 256);
        k = getNextToken(buffer, command, 0, 256, 10, blank);
        if (command[0] == 'a') {
             tempAccount = new AccountInfo();
             k = getNextToken(buffer, userLoginName, k, 256, 8, blank);
             (*tempAccount).setUsername(userLoginName);
             k = getNextToken(buffer, flag, k, 256, 2, blank);
            if (flag[1] == 'p') {
                 k = getNextToken(buffer, password, k, 256, 16, blank);
                (*tempAccount).setPassword(password);
            }
            cout << *tempAccount << endl;
            (*users).addUser(tempAccount);
        }
        else if (command[0] == 's') {
            (*users).showUsers();
        }
        else cout << "Command not found." << endl;
    }

    return 0;
}

Вывод выглядит так:

===============================================================================
>adduser bob -p password1
Login: bob
Password: password1

>adduser jack -p mypassword
Login: jack
Password: mypassword

>adduser jill -p pass1234
Login: jill
Password: pass1234

>showusers
Login: jill
Password: pass1234

Login: jill
Password: pass1234

Login: jill
Password: pass1234
===============================================================================

Вывод ДОЛЖЕН быть похож на это:

===============================================================================
>adduser bob -p password1
Login: bob
Password: password1

>adduser jack -p mypassword
Login: jack
Password: mypassword

>adduser jill -p pass1234
Login: jill
Password: pass1234

>showusers
Login: bob
Password: password1

Login: jack
Password: mypassword

Login: jill
Password: pass1234
===============================================================================

Примечание. Когда я изменяю main() (передавая информацию напрямую, а не получая ее из консоли с помощью cin), она выглядит следующим образом:

//=============================================================================
int main() {

    UsersDB* users = new UsersDB();
    AccountInfo* tempAccount;

    tempAccount = new AccountInfo("jack", "mypassword");
    (*users).addUser(tempAccount);

    tempAccount = new AccountInfo("jill", "pass1234");
    (*users).addUser(tempAccount);

    (*users).showUsers();

    return 0;
}

... Я получаю желаемый результат.

Большое спасибо.

Ответы [ 4 ]

2 голосов
/ 02 февраля 2012

Вы используете в своем классе указатели char *, они не работают как "обычные" строки. Когда вы передаете указатель char * методу и присваиваете его переменной-члену, вы не копируете строку, а просто заставляете переменную-член указывать на ту же строку, на которую указывает параметр. Поскольку все передаваемые параметры являются указателем на ваш входной буфер, следующий вход в буфер перезапишет текст во всех предыдущих объектах.

В C ++ вы действительно должны использовать std :: string вместо передачи char * для работы со строками, это очень поможет вам в такой ситуации и будет обрабатывать копирование и выделение памяти для новых строки автоматически.

Причина, по которой последняя версия с константами работает, заключается в том, что ваши объекты в конечном итоге указывают на текстовые константы, которые не перезаписываются, как ваш буфер.

1 голос
/ 02 февраля 2012

Вы никогда не выделяете место для хранения имен пользователей и паролей в вашем классе AccountInfo.Ваши функции setUserName и setPassword просто копируют необработанные указатели в класс.Таким образом, в вашем первом main каждый пользователь просто указывает на буферы username и userLoginName, которые вы объявили в main, поэтому они все одинаковы.Во втором main они указывают на строковые литералы, которые вы используете для их построения.

Я также добавил бы, что это по сути код C с функциями-членами.Если вы используете C ++, вы должны использовать функции C ++, такие как std::string и std::vector, которые делают подобные ошибки практически невозможными.

1 голос
/ 02 февраля 2012

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

//=============================================================================                                                                                                                                                              
int main() {
   char buffer[256];
   char *userLoginName;
   char *password;
   int i, j, k;
   char flag[3];;
   char command[11];
   char blank = ' ';

   UsersDB* users = new UsersDB();
   AccountInfo* tempAccount;

   while (!cin.eof()) { //while end of line is not reached                                                                                                                                                                                   
      cin.getline(buffer, 256);
      k = getNextToken(buffer, command, 0, 256, 10, blank);
      if (command[0] == 'a') {
         userLoginName = new char[9];
         password = new char[17];
         tempAccount = new AccountInfo();
         k = getNextToken(buffer, userLoginName, k, 256, 8, blank);
         (*tempAccount).setUsername(userLoginName);
         k = getNextToken(buffer, flag, k, 256, 2, blank);
         if (flag[1] == 'p') {
            k = getNextToken(buffer, password, k, 256, 16, blank);
            (*tempAccount).setPassword(password);
         }
         cout << *tempAccount << endl;
         (*users).addUser(tempAccount);
      }
      else if (command[0] == 's') {
         (*users).showUsers();
      }
      else cout << "Command not found." << endl;
   }

   return 0;
}

Просто не забудьте удалить их потом. Это наименьшее количество строк кода, чтобы исправить это, и я показываю это только для того, чтобы продемонстрировать, в чем проблема. Лучшим решением было бы вместо этого создать новые массивы в UserAccount при создании (так как кажется, что они вам всегда понадобятся) и удалить их в dtor следующим образом:

//At top of file:
#include <string.h>

AccountInfo::AccountInfo() {
   _username = new char[9];
   _password = new char[17];
}

AccountInfo::AccountInfo(char* username, char* password) {
   strcpy(_username, username);
   strcpy(_password, password);
}

AccountInfo::~AccountInfo() {
   delete _username;
   delete _password;
}

void AccountInfo::setUsername(char* username) {
   strcpy(_username, username);
}

void AccountInfo::setPassword(char* password) {
   strcpy(_password, password);
}
0 голосов
/ 02 февраля 2012

В вашей программе setPassword и setUsername хранят указатели на передаваемые в буферах, а не копируют строку.

Вам следует использовать функцию copyString.

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