Peter
Peter

Reputation: 4141

Instantiating a New Object in C++

I'm a Java/PHP programmer learning C++.

I've created a class called UserAccount and a class called Database.

I have an input file with five names:

Bob
Sam
Jane
Mark
Ann

I have the following (pseudo)code:

UserAccount *ua;
while (reading through the list of names, line by line) {
    ua = new UserAccount(name); // Create a new user based on this name
    Database->add(ua); // Add the user to the database
}

Database->listAllUsers();

The output should mirror the input file. Instead, I get:

Ann
Ann
Ann
Ann
Ann

I assume this has something to do with pointers, but I can't figure it out. I think I've provided enough information to identify the problem, but if the (pseudo)code above looks correct, I can provide more.

Upvotes: 0

Views: 162

Answers (4)

Rohit Vipin Mathews
Rohit Vipin Mathews

Reputation: 11787

Menu driven program using STL & Algorithm

U need a useraccount class to store the details of an individual user and database class to store the details of all useraccount s.

use string, stl, algorithms for efficiency.

A sample menu driven program for your need:

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>

using namespace std;

class useraccount
{
    string name;
    //other details

public:     
    void get(string str)
    {
        name=str;
    }
    void display()
    {
        cout<<name<<endl;
    }
};

void display_db(useraccount & e)
{
    e.display();
}

void main()
{
    vector<useraccount> database;
    useraccount *ptr;
    int choice;
    string name;    
    do
    {
        cout<<"\n\n1.Insert \n2.Delete\n3.Display\n4.Exit \nEnter your choice: \n";
        cin>>choice;
        switch(choice)
        {
        case 1:
            ptr=new useraccount;
            cout<<"Enter name:\n";
            cin>>name;
            ptr->get(name);
            database.push_back(*ptr);
            break;
        case 2:
            database.pop_back();
            break;
        case 3:
            for_each(database.begin(),database.end(),display_db);
            break;
        case 4:
            cout<<"Quiting";
            break;
        default:
            cout<<"wrong choice";
        }
    }while(choice!=4);
}

Upvotes: 1

Nathan S.
Nathan S.

Reputation: 5388

In C++, unlike other languages, you must explicitly think about where things are stored in memory. A pointer tells you where to look in memory to find something. An analogy would be to tell you to look at at the third letter in the 5th row on page 124 of a book. If my book is written in pencil, I could erase the words on that page and replace them with other words, and while the pointer would stay the same (that is, I look at the same location in the book), what is being pointed to would change.

This is what is happening in your code. Each time you read in a name, you are erasing the previous name and writing the new name in the same memory location. Thus, when you pass in a pointer in your constructor, you are in danger of the memory you are pointing to changing.

To fix this, you need to make a local copy of the name. This can be done by using the string class, as in the response by @Justin. But, for teaching purposes (this code is a bit more complicated), this is how you could allocate new memory internally (the string class just does it for you):

class UserAccount {
public:
    UserAccount(const char *name)
    {
        m_name = new char[strlen(name)+1];
        strcpy(m_name, name);
    }
    ~UserAccount()
    {
        delete [] m_name;
    }
private:
    char * m_name;
};

Note that we are now using the destructor, because we've allocated memory and need to free it again when the class is deleted. Also note that I haven't checked to see if the name being passed in is null, which should be done. A final note is that strings are implicitly null terminated to tell you when you've hit the end of the string. This is why we must allocate a large size -- to hold the null at the end of the string.

As @Marc pointed out, you can also use strdup, but I thought a longer explanation and showing the explicit allocation of memory would be useful to give a better sense of what is going on.

Upvotes: 1

Marc Butler
Marc Butler

Reputation: 1376

To follow up from my comments. You are likely running afoul of pointers, the std::string class will give you behaviour much more similar to that of Java.

The simplest solution will be to use the string strdup() call.

#include <cstring> // provides C string interface

UserAccount::UserAccount(char *n) {
    name = strdup(n);
}

UserAccount::~UserAcount() {
    // This is older C style, cleanup. Need or the memory will "leak".
    free(name);
    // You could also use: delete[] name;
}

Alternatively you could use std::string, but I'll leave that to your learning process. There is a lot of information on pointers out there, it will help your learning to understand pointers intimately.

// e.g.
char *str1 = "ann";
char *str2 = "bob";
str1 = str2;
// Both equal "bob".
str1 = "carol";
// Both equal "carol"!

Good luck.

Upvotes: 1

justin
justin

Reputation: 104698

Could you provide an example of the correct implementation?

This would be a start:

#include <string>
#include <vector>

class UserAccount {
public:
    UserAccount(const std::string& name) : d_name(name) {}
    ...
private:
    std::string d_name;
};

class Database {
public:
    Database() : d_accounts() {}

    void add(const UserAccount& account) {
        this->d_accounts.push_back(account);
    }

    ...

private:
    std::vector<UserAccount> d_accounts;
};

void zb() {
    Database db;
    db.add(UserAccount("Bob"));
    ...
}

It's different from what you posted because the vector's storing values of UserAccounts. At any rate -- more code would help. I'll eventually delete this because it's not a real answer(well, maybe update once the problem's defined better).

Upvotes: 2

Related Questions