C ++指针数组为每个元素返回相同的值

时间:2012-02-02 05:57:51

标签: c++ arrays pointers

我写了一个简化版本的代码,我正在努力说明我遇到的问题。我认为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 个答案:

答案 0 :(得分:2)

你在课堂上使用char *指针,它们不像“普通”字符串那样工作。当您将char *指针传递给方法并将其指定给成员变量时,您不会复制该字符串,而只是使成员变量指向与参数指向的字符串相同的字符串。由于传入的所有参数都是指向输入缓冲区的指针,因此缓冲区中的下一个输入将覆盖所有先前对象中的文本。

在C ++中,你应该使用std::string而不是传递char *来进行字符串操作,它会在这种情况下帮助你,并且会自动处理为新字符串复制和分配内存。

后一版本的常量有效的原因是你的对象最终指向不像缓冲区那样被覆盖的文本常量。

答案 1 :(得分:1)

您的问题是您的UserAccounts(所有这些)最终成为main()中(单个)用户和密码char数组的指针。 你可以通过每次使用这个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);
}

答案 2 :(得分:1)

您永远不会分配空间来存储AccountInfo课程中的用户名和密码。您的setUserNamesetPassword函数只是将原始指针复制到类中。因此,在您的第一个main中,每个用户只需指向您在username中声明的userLoginNamemain缓冲区,因此它们都是相同的。在第二个main中,它们指向用于构造它们的字符串文字。

我还要补充一点,这实际上是带有成员函数的C代码。如果你正在使用C ++,你应该使用像std::stringstd::vector这样的C ++功能,这样会使这样的错误几乎不可能。

答案 3 :(得分:0)

在你的程序中,setPassword和setUsername存储指向传入缓冲区的指针,而不是复制字符串。

您应该使用copyString函数。