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
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 );
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.
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")
Related
class Bank {
private:
string bankName;
vector<Account> accounts;
public:
Bank(string name){
bankName = name;
}
Account registerAccount(string name){
Account account(name, rand());
accounts.push_back(account);
return account;
}
void checkMyAccount(Account account){
cout << accounts.size();
cout << bankName;
for(Account acc : accounts){
if(acc.getBankId() == account.getBankId()){
cout << acc.getName();
}
}
}
};
I am new in c++, and do you know why in another method my vector become empty after pushing some data? this is full code https://repl.it/#mufti1/CircularFrontSigns
So the problem is that you have two different bank objects
Bank bank("BCA");
Account customerAccount = cust.requestAccount(bank);
Account requestAccount(Bank bank){
return bank.registerAccount(name);
}
The bank object in requestAccount is a copy of the bank object in main. So you add an account to the copy but not to the original in main.
To solve use a reference
Account requestAccount(Bank& bank){
return bank.registerAccount(name);
}
By using a reference Bank& the bank object is not copied, and your code alters the Bank object in main instead of the local copy.
For some reason beginners often assume that C++ passes objects by reference by default. But this is not the case, if you want a reference you have to ask for it.
Your problem is here:
Account requestAccount(Bank bank) //<--- you are passing by value
{
return bank.registerAccount(name);
}
When you call requestAccount(bank), you create a copy of the original Bank object:
Account customerAccount = cust.requestAccount(bank);
To fix this, change the function to:
Account requestAccount(Bank& bank) { //pass by reference
...
}
The problem is in code you did not show in the post : your requestAccount method takes a Bank object by copy instead of by reference. You should delete the Bank copy constructor (or make it private) to prevent these things from happening again.
Trying to build a database using classes.
This is just an excerpt of the classes, my main() creates a bunch of students using the class Student. Each student then has an ID and Name that are inputted later. Additionally, each student will have an array of 2 slots which will hold info for their courses they're taking. Those courses are created using the class Course.
What I'm trying to figure out is how can I place the course info (courseID and courseName) into a slot of the student's courses array once I assign them a course (in other words, student A is now in class 1. I want the courseID and courseName of class 1 to be assigned to student A's courses).
I try to use the locations of each course created in the main but that proves difficult trying to output. Is it possible to be in the class Student and have it call a function from class Course? Any help be great. Thanks.
class Course {
protected:
int courseID;
char* courseName;
public:
Course() {
courseID = 0;
courseName = "";
}
void makeID(int id, char* name) {
courseID = id;
courseName = name;
}
int getID() {
return courseID;
}
char* getCourseName() {
return courseName;
}
};
class Student : public Course {
private:
int studentID;
char* studentName;
int classCount;
int courses[2]; //could I change to: Course courses[2]?
char name[30];
int id;
public:
Student() {
studentID = 0;
studentName[30];
classCount = 0;
courses[2];
}
void makeStudent() {
cout << "Input 9 digit student ID: ";
cin >> id;
studentID = id;
cin.ignore();
cout << "Input first and last name of the student: ";
cin.getline(name, 30, '\n');
studentName = name;
return;
}
int getstudentID() {
return studentID;
}
int getclassCount() {
return classCount;
}
char* getstudentName() {
return studentName;
}
void addClass(int course) {
if (classCount == 2) {
cout << "Max amount of courses reached." << endl;
}
else {
courses[classCount] = course;
classCount++;
}
return;
}
int returnClass(int course) { //can't figure out what to do
return courses[course]; //here.
}
};
At very first, you should get a clear image how your data model shall look like.
OK, we have bunch of courses and a bunch of students. Courses and students are totally unrelated (apart from students attending courses) concepts at first, so it won't make any sense one inheriting from the other...
Imagining the scenario does not only cover one single period (school year, semester, trimester, ...), courses might change over time as well as will the students.
So you might have a vector or another data structure storing courses on one hand and students on the other.
Students will attend courses, this can be reflected in two ways:
courses holding lists of all students attending them
students holding lists of all courses they attend
Depending on use case, it might be appropriate to implement both redundantly.
How would you install such a relation ship? Easiest (at a first glance, at least) would be X holding one or more pointers to Y in both scenarios.
class Course
{
std::vector<Student*> participants;
public:
// whatever you need...
};
class Student
{
std::vector<Course*> students;
public:
// whatever you need...
};
As I now use pointers to represent the relation ship, the data structures holding all courses and students must not invalidate these if adding or removing students from! std::vector<X> would do so, so you cannot use it. Alternatives would be std::vector<std::unique_ptr<X>> or std::list<X>. Actually, std::vector<X*> would work as well, but then it would be you who would need to care for deletion, if removing courses or students, so prefer one of the other solutions.
But why did I not use smart pointers for the courses' and students' vectors? We'd be forced to use std::shared_ptr then for, but we won't profit from: If a student leaves our institution, we'd have to eliminate her/him anyway and remove her/him from all courses being attended. So we don't profit from smart pointers in this scenario (can be totally different in other ones). Analogously if a course is cancelled.
So make sure in the destructors the classes that
a course is removed from all students' vectors in its own vector
student is unregistered from all courses in its vector
With these basics, you can get ID, name and other data for a specific course a student attends simply via the pointer in the vector.
If you often seek via a specific information, (e. g. by name), you might have a std::map as well, facilitating the lookup by the specific attribute. You might use such a map for the complete list of courses/students as well, then, though, be aware that pointers are only guaranteed to remain valid in std::map<X, Y>, not in std::unordered_map<X, Y>, so if you intend to use the latter, you'd have to go the way via smart pointer again as with the vector: std::unordered_map<X, std::unique_ptr<Y>>.
Im new to programming sorry if the terms im using are wrong.
I have to create a console app where the use can add, modify, print and delete food items.
I made a class for my food
class food{
private:
bool isBasic;
nmfvg MeatOrOther;
string name;
public:
food::food(){
isBasic=true;
MeatOrOther = NONE;
name = "";
}
food::food(string _name,bool _isBasic, nmfvg _MeatOrOther){
isBasic=_isBasic;
MeatOrOther=_MeatOrOther;
name = _name;
}};
and im putting the food the user is making into an array of type food.
food foods[100];
food temp("food1",true,VEG);
foods[0]=temp;
1-Is this the right way for me to store foods?
2-If it is how do i go about printing the name of foods[0]?
As a better practice, you can keep the food objects in a std::vector instead of array like:
std::vector<food> foods;
foods.push_back(temp("food1",true,VEG));
To print name member, you may need a get method as public:
std::string food::GetName()
{
return name;
}
Then you can print the name by:
std::cout<<foods[0].GetName();
It's ok, but probably vector will be better.
You can print name, by adding accessor to name
class food {
//
public:
string get_name() const { return name; }
};
std::cout << foods[0].get_name() << std::endl;
or by use some function/operator << for output, or by making name public member and then simply use
std::cout << foods[0].name << std::endl;
I have been a follower for a long time but this is the first time I ask a question. In a nutshell, the issue is; vector<Student*> studentvector that is a vector of object pointers seems to push the student info back as student ; but when I print to see the first one in vector whether it performs as intended, I see it always updates the first record with new coming student info although there is no problem on studentvector.size() It pushes the record back to the vector as many as how many time I call addStudent(...) but it fills all vector with the information of last student. What may be done to succeed in filling vector with correct info within this frame, without using smart pointers or advanced stuff?
Sorry if I am vague on my question. You may lead me to provide what is also necessary to understand the problem. Thanks in advance.
addStudent(const string alias, const string name) throw(StudentException)
{
Student* student = new Student(alias, name)
studentvector.push_back(student);
cout << studentvector.front() << endl;
}
That is the implementation of Student;
#include "Student.h"
string *Alias;
string *Name;
Student::Student(string alias)
{
Alias = new string(alias);
}
Student::Student(string alias, string name)
{
Alias = new string(alias);
Name = new string(name);
}
Student::~Student()
{
delete Alias;
delete Name;
}
const string& Student::getAlias() const
{
return *Alias;
}
void Student::setAlias(const string& alias)
{
*Alias = alias;
}
const string& Student::getName() const
{
return *Name;
}
void Student::setName(const string& name)
{
*Name = name;
}
Consider alias is not reserved.
Doh! Here's your problem - all objects from type Student use the same global pointers:
string *Alias;
string *Name;
Make these two as members in you class
class Student
{
private:
string *Alias;
string *Name;
//..
};
EDIT:
Also, I don't think, that it's a good idea to use pointer to std::string, I'd suggest you to use like this:
class Student
{
private:
string Alias;
string Name;
//..
};
Your Student.cpp defines a single global pointer Alias and a single pointer Name. What you really want is a separate Alias and Name for each Student object. You do this by adding members to the class:
class Student {
public:
Student(const std::string& a, const std::string& n);
//...
private:
std::string Alias;
std::string Name;
};
Student::Student(const std::string& a, const std::string& n)
: Alias(a), Name(n)
{}
I may be delirious here, but are you not specifically printing out front, while pushing back?
studentvector.push_back(student);
cout << studentvector.front() << endl;
You're pushing onto the back, not the front, then printing what's in front. Of course you're not seeing front changing. You need to print back or push front. If push front isn't available, you can use insert(container.begin(), object).
You also need to move those global string variables into the class as members, so that for each instance of Student, the student has instances of Name and Alias.
Another note... you're dynamically allocating a string class. The purpose of the string class is to handle the dynamic memory of a char* string for you. There's no reason for you to use string*s in this situation, as far as I can tell from your code. string will handle the new and delete internally for you.
I agree with what everyone else has said. However, I can't reproduce the problem you say you're having. In particular, the following (rather icky) code correctly outputs two lines saying "Student(foo,bar)".
#include <iostream>
#include <vector>
#include <string>
// NOTE WELL: many things in this code are bad style and should not be imitated.
// One of them is the namespace-polluting using-directive below:
using namespace std;
struct Student {
string alias, name;
Student(string a, string n) : alias(a), name(n) {}
};
class StudentException : public exception {};
vector<Student*> studentvector;
ostream& operator<<(ostream& stream, Student* student) {
stream << "Student(" << student->alias << "," << student->name << ")";
return stream;
}
void addStudent(const string alias, const string name) throw(StudentException)
{
Student* student = new Student(alias, name);
studentvector.push_back(student);
cout << studentvector.front() << endl;
}
int main(void) {
addStudent("foo","bar");
addStudent("baz","quux");
}
It might be helpful to know how your (not-working) code diverges from the above.
A couple of remarks that conceivably might be relevant:
You aren't by any chance confusing the front and back ends of the vector somehow?
If (unlike your addStudent function) your actual code has a single Student object, and is modifying it and then pushing a pointer to it onto your vector, then of course you'll get the kind of wrong results you describe, because it'll be the same pointer every time.
std::vector::front() will return you the reference to the first element in the vector.
If you want to remove element, you need to call pop_back() which will return the element and remove from the vector.
If you have Boost then check out Boost Pointer Containers.
I'm working on a project where I create bank accounts and able to deposit and withdraw. I am to create two bank account and two people- one one the stack and the other on the heap. I should deposit and withdraw into each twice and get the balance print the name and ID and account numbers. At the moment I'm get what I believe is a site fault , reading or writing to protected memory. I've left comments on where I think the errors lie. I would appreciate any help. Thanks.
#include <iostream>
#include <string>
using namespace std;
class BankAccount {
private:
double *balance;
int *accountNumber;
public:
BankAccount(){//default constructor
*balance = 0.0;/***This is where is says the Access violation lies*/
*accountNumber = 0;
}
BankAccount(double bal, int acctNum){//constructor
balance = new double(bal);
accountNumber = new int(acctNum);
}
~BankAccount() {delete balance; delete accountNumber;}
void Deposit(double amt) {
*balance = *balance + amt;
}
virtual double GetBalance() {
return *balance;
}
virtual double GetAccountNumber() {
return *accountNumber;
}
virtual double Withdraw(double amt) {
*balance = *balance - amt;
return *balance;
}
};
class Person {
string *name;
int *ID;
public:
Person(){//default constructor
*name = "name not yet defined";
*ID = 0;
}
Person(string nameIn, int idIn){//constructor
name = new string(nameIn);
ID = new int(idIn);
}
virtual int GetID() {
return *ID;
}
virtual string GetName() {
return *name;
}
};
class NamedBankAccount: public BankAccount {
private:
Person *owner;
public:
NamedBankAccount(){
}
NamedBankAccount(Person *p): owner(p){/***This is where is says the Access violation lies*/
p = new Person();
}
~NamedBankAccount(){delete owner;}
Person getPerson() {
return *owner;
}
};
int main() {
Person *q = new Person("Joe", 54321);
cout << q->GetName() << endl;
cout << q->GetID() << endl;
NamedBankAccount nba1;/***This is where is says the Access violation lies*/
NamedBankAccount *nba2 = new NamedBankAccount(q);
nba1.Deposit(50);
nba1.Deposit(50);
nba1.Withdraw(25);
cout << nba1.GetBalance() <<endl;//should print 75
nba2->Deposit(60);
nba2->Deposit(60);
nba2->Withdraw(20);
cout << nba2->GetBalance() << endl;//should print 100
getchar();
return 0;
}
Do not use pointers here. Just have those strings and integers be member variables. For the specific problem - you haven't allocated any memory before assignment in the default constructor.
Do something like:
class BankAccount {
private:
double balance;
int accountNumber;
public:
BankAccount() :
balance( 0.0 ),
accountNumber( 0 ) {}
// ...
Edit:
Couple of more points about your code:
make use of initialization list in the constructors instead of assignment to member variables - this avoids two-step process of first default-initializing the members and then assigning to them
base polymorphic classes should have virtual destructors, so instances of derived classes could be properly destroyed via pointer to base
polymorphic types usually need to follow the rule of three to avoid slicing
do not make all member functions of a base class virtual, only those you want derived classes to override
think before making a type polymorphic - do you really have bank accounts without owners? Maybe that can be just a value type?
make accessor methods const, so you can get information from const instances
check for errors (you sure don't want to allow withdrawals from zero or negative balance accounts)
"do not use pointers" is a bit strong but what Nikolai means is that member variables shouldn't be pointers to base types but just those types
i.e. in BankAccount, balance should just be an double and not a double* like wise for the others
or have BankAccount() call BankAccount(0.0, 0) as that will allocate the fields right like wise for Person() but unexpectedly this doesn't do what i thought it would in C++ as Karl Knechtel remarks
You are dereferencing an uninitialized pointer, if you change their places it would still do the same thing.
You see, c++ (and c) uses pointers as addresses to memory, if you don't initialize then they will point to anywhere in memory, so dereferencing will PROBABLY cause access violation (probably because you don't know were your pointer points to).
The correct way would be:
BankAccount(){//default constructor
balance = new double; // Initialize pointer (make it points to a valid memory address)
*balance = 0.0; // Give a value to the variable where balance is pointing
accountNumber = new int; // Initialize pointer (make it points to a valid memory address)
*accountNumber = 0; // Give a value to the variable where balance is pointing
}
OR, if you want to allocate memory latter:
BankAccount(){//default constructor
balance = 0; // Make it point to address 0 (conventional value meaning it is uninitialized)
accountNumber = 0; // Make it point to address 0 (conventional value meaning it is uninitialized)
}
Of course, as stated, in your case it would probably be best to use normal variables and not pointers. You should read more about pointers before using them, they can be a pain (I think I speak here on behalf of 99.999% of C and C++ programmers, we've all been there).