Edu
Edu

Reputation: 2643

Storing a pointer in class

I'm learning C++ and having trouble with pointers.
This simple project consists in a invoice that has a pointer to a customer.

Classes:

class Customer {
    string name;
public:
    Customer(string name) { this->name = name; };
    string getName() { return name; };
    void changeName(string name) { this->name = name; };
};

class Invoice {
    Customer * customer;
public:
    Invoice(Customer *customer) { this->customer = customer; };
    Customer getCustomer() { return *customer; };
};

Main:

Customer *customer1 = new Customer("Name 1");
Invoice invoice1(customer1);

cout << invoice1.getCustomer().getName() << endl; //Return:Name 1;

How can I use Customer::changeName(string name) in order to make this work:

(...) changeName("Name 2");

cout << invoice1.getCustomer().getName() << endl; //Return:Name 2;

I don't know what I should use to change the customer's name. Or maybe I'm doing something wrong in the class Invoice.

Why change the name through Invoice?
So I can learn how I can learn how to use the pointer before the project starts getting big.
Later I'm going to have a vector of Invoices and a vector of Customers. Getting the pointer to a Customer from a Invoice or from a vector of Customers should be the same.

Thank you,
Eduardo

Upvotes: 0

Views: 3639

Answers (3)

WhozCraig
WhozCraig

Reputation: 66194

If you want this substantially hardened, i've taken such liberties here. Both Customer and Invoice declarations are significantly updated. Compare them to your existing code. Don't just copy this into your code, as it will definitely break a ton of things. Rather, look at it and see if it makes sense to you:

class Customer 
{
    string name;

public:
    Customer(const string& name) : name(name) {};

    const string& getName() const { return name; };
    void changeName(const string& name) { this->name = name; };
};

class Invoice 
{
    const Customer& customer;

public:
    Invoice(const Customer& customer) : customer(customer) {};

    const Customer& getCustomer() const { return customer; };
};

In general (more often than not, anyway) the only times you should need to pass an object by pointer is if there is a chance the object pointer receiver should accept NULL as a valid value. Otherwise use references or smart pointers. Arrays of object pointers to support polymorphic access not withstanding (and even then, smart pointers ftw), this is generally a good rule to follow.

Significant changes made:

  • Uses const references unless there is specific need for non-const-access
  • Classes have initializer lists to ensure best-construction for member vars, and, in fact are required now for Invoice, since the Customer reference memeber must be initialized in an initializer list.

Main

Customer customer1("Name 1");
Invoice invoice1(customer1);

// note: invoice now only allows you to obtain a const-reference
//  to the customer of the invoice. As such, you can only fire
//  const-members on the returned customer reference.
cout << invoice1.getCustomer().getName() << endl; //Return:Name 1;

// without the const-ness of the getCustomer() member and the reference
//  it returns, you would have been able to do this:
//
//  invoice.getCustomer.changeName("newname");
//
// As it is written now, you can only change a customer name from
//  a non-const customer reference (or pointer), and in doing so, 
//  *all* invoices for that customer will reflect this change.
customer1.changeName("Name 2");

// note: the invoice was not changed, but the customer it references
//  was, and we should see that change now.
cout << invoice1.getCustomer().getName() << endl; //Return:Name 2;

I hope this gives you some ideas on how to restrict and harden your object access later on in your project.

Upvotes: 2

Caribou
Caribou

Reputation: 2081

Customer getCustomer() { return *customer; };

should be

Customer& getCustomer() { return *customer; };

because in the first case you copy the customer object and so your changes happen in a temporary object that gets thrown away...

in the second you will return a reference to the object you created.

to change name

string newName = "Edu";
invoice1.getCustomer().changeName( newName );

Upvotes: 7

ezod
ezod

Reputation: 7421

In Invoice, return the pointer to the Customer itself, rather than a copy of its dereferenced value:

Customer* getCustomer() { return customer; };

Then you can change the name like so, and the change will affect the actual Customer object:

invoice1.getCustomer()->changeName("Name2")

Upvotes: 1

Related Questions