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).
Related
What I am trying to achieve is creating a superclass array of subclass objects.
In this particular test I'm working on, I want to have an animal array that has some dog objs and some cat objs while they maintain their attributes.
#include <iostream>
using namespace std;
//ANIMAL
class animal
{
protected:
int ID;
string name;
public:
animal(string = "Unknown");
int get_ID() { return ID; }
virtual string get_name() { return name; }
};
animal::animal(string n) { name = n; }
//DOG
class dog : public animal
{
static int newID;
string sound;
public:
dog(string = "Corgi", string = "Woof!");
string get_name() { return sound + " " + name; }
};
int dog::newID = 0;
dog::dog(string n, string s) : animal(n)
{
newID++;
ID = newID;
cout << ID << "\t";
sound = s;
}
//CAT
class cat : public animal
{
static int meowID;
string color;
public:
cat(string = "Munchkin", string = "Calico");
string get_name() { return color + " " + name; }
};
int cat::meowID = 89;
cat::cat(string n, string c) : animal(n)
{
meowID++;
ID = meowID;
cout << ID << "\t";
color = c;
}
//MAIN
int main(int argc, char* argv[])
{
animal** test;
animal* p;
for (int i = 0; i < 6; i++)
{
p = new dog;
p++;
}
cout << "\n";
for (int i = 0; i < 6; i++)
{
p = new cat;
p++;
}
cout << "\n";
test = &p;
cout << (*test-7)->get_ID();
return 0;
}
What I've learned so far is that p isn't an array, and it keeps pointing to different memory addresses through the loops.
I cannot do animal** test = new dog[6]; as it is an invalid initialization. Even if that worked I would have trouble cascading another array segment of cat.
This is the output I obtained:
1 2 3 4 5 6
90 91 92 93 94 95
0
The first line is displaying dog IDs being invoked 6 times, and the second line is displaying cat IDs being invoked 6 times. (*test-7)->get_ID();is the last number.
It seems the constructors are being invoked right. However, I have no idea where my pointer is pointing, since I am expecting 91 not 0.
How do I get an animal array that I can access information from each element? For example,
animal** myArray;
{do something}
cout << myArray[2].get_name() << endl << myArray[7].get_ID();
and it outputs
Woof! Corgi
91
One important detail about the animal class: polymorphic types can run into issues when their destructors are called but those destructors are not virtual. It is recommended that you make the destructor of the base class virtual, even if that class itself does not actually need a destructor. In this case, you can tell the compiler that you want the destructor to be virtual but generate a default implementation of it with:
virtual ~animal() = default;
Add the above line in the public: section of your animal class. This ensures that any derived classes that you define later on will get a virtual destructor automatically.
Now to the rest of your code:
p = new dog;
So far, so good. But then this:
p++;
does nothing useful other than making the pointer point to an invalid address. Then in the next iteration, another p = new dog; will be performed. The previous dog object you allocated is now lost forever. You got a so-called "leak".
It seems you expect new to allocate objects an a way that lays them out in memory one after another. That is not the case. new will allocate memory in an unpredictable location. As a result, this:
*test-7
cannot possibly work, as the objects are not laid out in memory the way you expected. What you get instead is an address to some memory location 7 "positions" before the latest allocated object that pretty much certainly does not point to the animal object you were hoping. And when you later dereference that you get undefined behavior. Once that happens, you cannot reason about the results anymore. They can be anything, from seeing wrong text being printed to your program crashing.
If you want an array of animal pointers, you should specifically create one:
animal* animals[12];
This creates an array named animals that contains 12 animal pointers. You can then initialize those pointers:
for (int i = 0; i < 6; i++) {
animals[i] = new dog;
}
cout << "\n";
for (int i = 6; i < 12; i++) {
animals[i] = new cat;
}
You then just specify the array index of the one you want to access:
cout << animals[0]->get_ID() << '\n'; // first animal
cout << animals[6]->get_ID() << '\n'; // seventh animal
Don't forget to delete the objects after you're done with the array. Since animals is an array, you can use a ranged for loop to delete all objects in it:
for (auto* animal_obj : animals) {
delete animal_obj;
}
However, all this low-level code is quite tedious and error-prone. It's recommended to instead use library facilities that do the allocations and cleanup for you, like std::unique_ptr in this case. As a first step, you can replace your raw animal* pointer with an std::unique_ptr<animal>:
unique_ptr<animal> animals[12];
(Don't forget to #include <memory> in your source file, since std::unique_ptr is provided by that library header.)
Now you've got an array of smart pointers instead of raw pointers. You can initialize that array with:
for (int i = 0; i < 6; i++) {
animals[i] = make_unique<dog>();
}
cout << "\n";
for (int i = 6; i < 12; i++) {
animals[i] = make_unique<cat>();
}
Now you don't need to delete anything. The smart pointer will do that automatically for you once it goes out of scope (which in this case means once the animals array goes out of scope, which happens when your main() function exits.)
As a second step, you can replace the animals array with an std::vector or an std::array. Which one you choose depends on whether or not you want your array to be able to grow or shrink later on. If you only ever need exactly 12 objects in the array, then std::array will do:
array<unique_ptr<animal>, 12> animals;
(You need to #include <array>.)
Nothing else changes. The for loops stay the same.
std::array is a better choice than a plain array (also known as "built-in array") because it provides a .size() member function that tells you the amount of elements the array can hold. So you don't have to keep track of the number 12 manually. Also, an std::array will not decay to a pointer, like a plain array will do, when you pass it to functions that take an animal* as a parameter. This prevents some common coding bugs. If you wanted to actually get an animal* pointer from an std::array, you can use its .data() member function, which returns a pointer to the first element of the array.
If you want the array to be able to grow or shrink at runtime, rather than have a fixed size that is set at compile time, then you can use an std::vector instead:
vector<unique_ptr<animal>> animals;
(You need to #include <vector>.)
This creates an empty vector that can store elements of type unique_ptr<animal>. To actually add elements to it, you use the .push_back() function of std::vector:
// Add 6 dogs.
for (int i = 0; i < 6; i++) {
animals.push_back(make_unique<dog>());
}
// Add 6 cats.
for (int i = 0; i < 6; i++) {
animals.push_back(make_unique<cat>());
}
Instead of push_back() you can use emplace_back() as an optimization, but in this case it doesn't matter much. They key point to keep in mind here is that a vector will automatically grow once you push elements into it. It will do this automatically without you having to manually allocate new elements. This makes writing code easier and less error-prone.
Once the vector goes out of scope (here, when main() returns,) the vector will automatically delete the memory it has allocated to store the elements, and since those elements are smart pointers, they in turn will automatically delete the animal objects they point to.
If you're new to C++, it's important that you get started on the right foot and to follow modern best practices, namely:
Avoid using pointers, new, delete, new[] and delete[].
Instead use smart-pointers (unique_ptr, shared_ptr, but don't use auto_ptr!).
Use the make_ functions instead of new. That way you don't need to worry about delete.
See Advantages of using std::make_unique over new operator
Use std::vector<T> (and std::array<T,N> if you have fixed-size collections) instead of new[] or p** (and never use malloc or calloc directly in C++!)
I note that you should also generally prefer Composition over Inheritance, but with trivial examples like yours it's difficult to demonstrate the concept because a Dog and a Cat "are" Animals.
I also note that when the possible set of subclasses is known at compile-time you should consider using a union-type instead of subclassing because it allows consumers to exhaustively work with returned values without needing to use RTTI or guesswork.
This can be done with using AnAnimal = std::variant<cat,dog>.
Anyway, this is what I came-up with. The class animal, class dog, and class cat code is identical to your posted code (and is located within the // #region comments), but the #include and using statements at the top are different, as is the main method.
Note that my code assumes you have a compiler that complies to the C++14 language spec and STL. Your compiler may default to C++11 or older. The std::make_unique and std::move functions require C++14.
Like so:
#include <iostream>
#include <memory>
#include <vector>
#include <string>
// Containers:
using std::vector;
using std::string;
// Smart pointers:
using std::unique_ptr;
using std::move;
using std::make_unique;
// IO:
using std::cout;
using std::endl;
// #region Original classes
//ANIMAL
class animal
{
protected:
int ID;
string name;
public:
animal(string = "Unknown");
int get_ID() { return ID; }
virtual string get_name() { return name; }
};
animal::animal(string n) { name = n; }
//DOG
class dog : public animal
{
static int newID;
string sound;
public:
dog(string = "Corgi", string = "Woof!");
string get_name() { return sound + " " + name; }
};
int dog::newID = 0;
dog::dog(string n, string s) : animal(n)
{
newID++;
ID = newID;
cout << ID << "\t";
sound = s;
}
//CAT
class cat : public animal
{
static int meowID;
string color;
public:
cat(string = "Munchkin", string = "Calico");
string get_name() { return color + " " + name; }
};
int cat::meowID = 89;
cat::cat(string n, string c) : animal(n)
{
meowID++;
ID = meowID;
cout << ID << "\t";
color = c;
}
// #endregion
int main()
{
// See https://stackoverflow.com/questions/44434706/unique-pointer-to-vector-and-polymorphism
vector<unique_ptr<animal>> menagerie;
// Add 6 dogs:
for( int i = 0; i < 6; i++ ) {
menagerie.emplace_back( make_unique<dog>() );
}
// Add 6 cats:
for( int i = 0; i < 6; i++ ) {
menagerie.emplace_back( make_unique<cat>() );
}
// Dump:
for ( auto &animal : menagerie ) {
cout << "Id: " << animal->get_ID() << ", Name: \"" << animal->get_name() << "\"" << endl;
}
return 0;
}
I am new to classes and constructors. This program requires user to input name for two circles. I defined a default constructor to set parameters for radius and name and another constructor to accept them as arguments. I believe there is an issue with setName and also it tells me the constructors have already been defined. Any help is appreciated!
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
class Circle
{
private:
double pi = 3.14;
double radius;
string *name;
public:
Circle();
Circle(double, string);
Circle::Circle()
{
radius = 0.0;
*name = nullptr;
}
Circle::Circle(double r, string n)
{
radius = r;
*name = n;
}
~Circle()
{
delete[] name;
}
void setRadius(double r)
{
if (r >= 0)
radius = r;
else
{
cout << "Invalid radius\n";
exit(EXIT_FAILURE);
}
}
double getRadius()
{
return radius;
}
double getArea()
{
return pi* radius * radius;
}
double getCircumference()
{
return 2 * pi * radius;
}
void setName(string n)
{
*name = n;
}
string getName()
{
return *name;
}
};
int main()
{
Circle circle1;
Circle circle2;
double circRad1;
double circRad2;
string name1;
string name2;
cout << "Enter the name for circle 1: ";
getline(cin, name1);
cout << "Enter the name for circle 2: ";
getline(cin, name2);
cout << "Enter the radius for cirle 1: ";
cin >> circRad1;
cout << "Enter the radius for cirle 2: ";
cin >> circRad2;
circle1.setRadius(circRad1);
circle2.setRadius(circRad2);
circle1.setName(name1);
circle2.setName(name2);
cout << "Circle 1 name: " << circle1.getName() << "\n";
cout << "Circle 1 radius: " << circle1.getRadius() << "\n";
cout << "Circle 1 area: " << circle1.getArea() << "\n";
cout << "Circle 1 circumfrence: " << circle1.getCircumference() << "\n";
cout << "\n";
cout << "Circle 2 name: " << circle2.getName() << "\n";
cout << "Circle 2 radius: " << circle2.getRadius() << "\n";
cout << "Circle 2 area: " << circle2.getArea() << "\n";
cout << "Circle 2 circumfrence: " << circle2.getCircumference() << "\n";
return 0;
}
Problems I see:
Constructors
You have:
Circle();
Circle(double, string);
Circle::Circle()
{
radius = 0.0;
*name = nullptr;
}
Circle::Circle(double r, string n)
{
radius = r;
*name = n;
}
That is not correct since the first two lines declare the constructors while you declare, and define, them again with incorrect syntax.
Remove the first two lines.
Use of name
It's not clear why you are using string* for name. Make it an object, not a pointer.
string name;
Then, change the constructors to:
// Use the default constructor to initialize name
Circle() : radius(0.0) {}
Circle(double r, string n) : radius(r), name(n) {}
You may remove the destructor altogether. If you insist on having one, change it to (there is no need for delete name any more):
~Circle() {}
Change setName() to:
void setName(string n)
{
name = n;
}
Change getName() to:
string getName() const
{
return name;
}
PS Your attempted code indicates to me that you will benefit from going through the fundamentals of the language from a good book. See The Definitive C++ Book Guide and List for ideas.
I just want to add to the previous answer since I don't have enough points to comment.
In his constructor, he's using what's called an initializer list:
i.e.
Circle() {
foo = 0;
bar = 0;
}
versus (initializer list):
Circle() : foo(0), bar(0) {}
When you're just initializing variables, the preferred practice is almost always the list format. This is because it provides the arguments to construct your object before it is instantiated. This would allow you to construct objects whose "identity" isn't known until runtime (i.e. a variable-type object, or an object which could take on more than one type, although these aren't quite native to c++), or a const value.
I am a little suspicious about your instructor having you create dynamic objects in your first programming course, but since this seems to be the case,
The reason you can't simply pass in a string object and assign the pointer to it is because a pointer is strictly an address to an already existing object. This would only work if the string were passed in by reference, and then the syntax might be:
void foo(std::string& str) {
name = &str;
}
When you pass in by value (without the ampersand) a copy of your object is being made to pass in via the parameter. This copy doesn't exactly have it's own home in memory yet, and it's definitely not the same home as what you passed in the parameter. So when you try to give it's address to the pointer, the compiler wants to complain because the address you're trying to save is going to disappear as soon as this scope is over with (the next } is hit).
You can, however, create a permanent object with a copied value. This is when you allocate dynamic memory on heap (normally it's on the stack). This would look something like:
void foo(std::string str) {
name = new std::string(str);
}
This will allow your name pointer to point to a newly created object on the heap. This is why you need the delete[] expression in your deconstructor, because the compiler can't manage dynamic memory for you, so you have to make sure to free it before the program ends.
Note that the [] are needed because a string is actually an array of characters. When you dynamically allocate an array, the [] notation will ensure that the memory until a sentinel value is read is freed. A sentinel character almost always refers to NULL or 0 on the ASCII chart.
If it were an int being freed, the syntax would just be:
delete x;
One last note. In your private section you have a variable called pi which is default initialized to 3.14. This is presumably because this is a value which will often be referred to and is common amongst all circles. When you have such common variables which will be the same in every instance of that class, you'll want to use what are called static variables. This is a variable which is allocated once, and which everyone associated with that variable has access to. Also, because you don't want pi to change, it should be const. It might look like this:
private:
static const double PI = 3.14;
What this will do is create one object called PI, and that exact same PI object will be used in every single circle you create. This will vastly cut down on the memory usage of that object, assuming you may create many. It is also good to note that typically const variables are capitalized, and non-const variables are not.
I agree with all of the points made by #RSahu, but will attempt to answer your specific issues.
Disclaimer: Using pointers as you do in this assignment is unnecessary and dangerous. It is even more unusual to require the use of them in this situation as pointers are a notoriously difficult concept for beginners to grasp.
Defining Constructors
You are defining each constructor twice.
Circle();
Circle::Circle()
{
// ...
}
and then
Circle(double, string);
Circle::Circle(double r, string n)
{
// ...
}
You only need to define them once. If you are declaring and defining them at the same time then the following is sufficient:
Circle()
{
// ...
}
If you want to declare and define them separately then you can do:
class Circle
{
public:
// Declare the constructor
Circle();
};
// Then later in some source, define it
Circle::Circle()
{
// ...
}
Implementing Constructors
You have crucial mistakes in both constructors (ignoring the fact that you are forced to use string*).
First,
Circle()
{
radius = 0.0;
*name = nullptr;
}
When you perform *name = nullptr you are dereferencing the name pointer and assigning it to nullptr.
This is bad for multiple reasons:
name has not been set. You are dereferencing a garbage pointer and setting it to nullptr. This is a crash.
Even if name had been initialized, you are setting the string object that it points to to nullptr which is another crash.
The proper way to initialize this would be as:
Circle()
: radius{ 0.0 },
name{ nullptr }
{
}
Let us look at the other constructor now.
Circle(double r, string n)
{
radius = r;
*name = n;
}
Again, radius is set correctly (mostly) but we have major issues with name.
name once again is uninitialized. So we are setting a non-existant string that name points to to n.
Now, here we are actually spared a bit of good luck. If you instead were performing
name = &n;
Then that would be bad as n is a temporary object. Once we leave the constructor our name would be pointing to garbage and you would crash the next time you try to access it.
But so how do we fix this constructor? I would do it like so:
Circle(double const r, string n)
: radius{ r },
name{ new string{n} }
{
}
In name{ new string{n} } we are setting name to a new string object that is initialized by the value in n.
Hope you are beginning to understand why in my disclaimer I do not approve of the requirement of using string* ...
Fixing setName
So, your implementation of setName is almost OK.
If we created an object of Circle using the second constructor it would be fine. Our string that name points to would simply be set the value of n.
But what if we are using a Circle created via the first constructor? Then we would be dereferencing a nullptr and attempting to set it the value of n. Crash.
I would actually fix this problem in your first constructor by changing it to:
Circle()
: radius{ 0.0 },
name{ new string }
{
}
So now we know name always points to a valid string object.
Finally, the Destructor
In the destructor you are using the incorrect delete[].
Use delete[] when deleting a dynamic array of objects. string is a single object, and thus should use delete.
I personally also think it is a good habit to set any deleted pointer to nullptr so that any common nullptr checks will work and not fail due to garbage.
~Circle()
{
delete name;
name = nullptr;
}
Question
Design a class Employee with name and employee number. Derive Manager, Scientist and Laborer classes. The manager class has extra attributes title and dues. The scientist class has extra attribute number of publications. The Laborer class has nothing extra. The classes have necessary functions for set and display the information.
My solution
#include<iostream>
#include<cstring>
using namespace std;
class employee
{
protected:
char *name;
int number;
public:
employee()
{
cout<<"enter employee name \n";
cin>>name;
cout<<"enter employee number \n";
cin>>number;
}
void display()
{
cout<<"name \t"<<name<<endl;
cout<<"number \t"<<number<<endl;
// inside class function is a inline function
}
};
class manager: private employee
{
float due;
char *title;
public:
manager( )
{
cout<<"due\t "<<endl;
cin>>due;
cout<<"title\t"<<endl;
cin>>title;
fflush(stdin);
}
void display()
{
employee::display(); //inside class function is a inline function
cout<<"due\t"<<due<<endl;
cout<<"title\t"<<title<<endl;
//inside class function is a inline function
}
};
class labour :private employee
{
public:
void display()
{
employee::display(); //inside class function is a inline function
}
};
class Scientist :private employee
{
int number;
public:
Scientist()
{
cout<<"publication number "<<endl;
cin>>Scientist::number;
}
void display()
{
employee::display();
cout<<" pub number "<<Scientist::number<<endl;
fflush(stdin);
} //inside class function is a inline function
};
int main()
{
manager m;
m.display();
Scientist s;
s. display();
labour l;
l.display();
return 0;
}
You don't allocate any memory for title or name, so you can't read into them from std::cin. Instead of using char* you should use std::string which will do all of the allocation for you:
std::string title;
std::string name;
In the constructor of empolyee you read into an uninitialized char*. Therefore it does not point to a valid block of memory where you could store the entered name to. You could do
name = static_cast<char*>(malloc( 32 * sizeof(char) ));
to allocate memory such that name points to valid memory, but you always waste memory or do not have enough for the input. Also you have to free the memory in the destructor.
As Peter Schneider wrote in the comment of this answer, another option is to use arrays of a fixed size as a member, e.g.
char name[MAX_NAME_LENGTH];
with a e.g. preprocessor defined
#define MAX_NAME_LENGTH 64
at the top of your file. This way the copy constructor does his job. With pointers as members, you always have to write them yourself, otherwise, the original class instance and copied instance will have a member pointer pointing to the same memory. So if one the copied instance changes the name, the original instance will have a changed name, too.
The easiest solution would be to use a std::string instead of char*. It allocates memory on its own and you don't have to free anything and copying works fine, too.
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")
As you can see I am new to C++, but I can't understand why y = new Person() in function foo is wrong. Thanks for your help.
I get the this error:
error: no match for ‘operator=’ in ‘y = (((Person*)operator new(32u)),
(, ))’
UPDATE:
I will accept the answer with the most upvotes by tonight or the one that is more convincing.
The argument between me and my friend is wether the function foo can change the object and propagate the change outside the function like when doing y = Person(), then also brother will change or will it remain intact?
.
CODE:
#include <iostream>
using namespace std;
class Person {
public:
int age;
char name[25];
Person() {
age = 0;
}
};
void foo(Person &y)
{
y = new Person();
}
int main()
{
Person *brother = new Person();
brother->age = 20;
cout << "age = " << brother->age << endl;
foo(*brother);
cout << "age = " << brother->age << endl;
return 0;
}
You probably come from a language were objects can only be created with new. In C++, this is not the case. And unless you really need it, you should not use new. Just create it as a normal variable:
#include <iostream>
#include <string>
class Person
{
public:
unsigned age;
std::string name;
Person(unsigned age, std::string name)
: age(age)
, name(std::move(name)) // move is C++11
{}
};
int main()
{
Person brother(8, "Tim John");
std::cout << "age = " << brother.age << '\n';
// Edit regarding the question in the comments:
brother = Person(16, "John Tim");
std::cout << "age = " << brother.age << '\n';
}
Your problem with the code above is that new returns a Pointer, and you are trying to assign a pointer to a Person, which obviously can't work.
void foo(Person &y)
{
y = new Person();
}
y is a reference, not a pointer. To reassign to y, you'd use
y = Person();
but if you really want to allocate a new person, you'd use
void foo(Person* &y) // reference to pointer to Person
With a reference, you basically say that you modify the value at the calling site.
Note that your current code leak. If you have a bare pointer that you want to manage yourself, you have to delete it first:
void foo (Person*& y)
{
delete y;
y = new Person;
}
But as you see, the code is already becoming messy without knowing your target. It might be more appropriate to delete at the calling site, or to not allocate y at all before calling foo(...).
Also note that using foo (Person* y) instead would not solve the issue of newing at the calling site:
void foo (Person *y)
{
y = new Person();
}
This of course compiles, but modifies only foo's own y variable. The caller will have an unchanged pointer.
Note that you'd better use value types or smart pointers, as it is non-trivial to write exception safe code that manually manages memory.
In function foo the line should be
y = Person();
y is not a pointer.
EDIT: actually, this is the wrong answer (even though you're currently accepted it from Jon). You are not supposed to mix heap and stack, and cause memory leaks like that. The right way to do it is to change members of the object directly. Assignment operator (operator=) will change the members of the object. Because the question is not about mixing heap and stack, but about changing object here's the code that better explains the problem. Note that there's no new here to complicate the issue.
void foo(Person &y)
{
y = Person();
}
int main()
{
Person brother;
brother.age = 20;
...
foo(brother);
...
return 0;
}
After y = Person() the brother object will be changed because y is brother and assignment operator changes the members of the object.
Never confuse yourself with the & and * operators when dealing with pointer operations.
'&' is used in different context.
&, when used in a function's formal parameters is a reference parameter , this operator passes a variable by reference(by its address).However the variable y still acts like a normal variable.
So this block of code..
void foo(Person &y)
{
y = new Person();
}
would not work as new Person() is parsing a pointer to a variable.
For example,
int * intp = new int;
int variable = intp;
this is the type of thing that's happening here.A reference parameter acts like a variable but actually has direct access to the variable due to the fact that it's a call by referance operation.
The correct way to write this function will look like this
void foo(Person ** y)
{
*y = new Person();
}
That is if you're trying to initialize a class pointer via a function.
As cooky said this is a misconception people make in c++ whom program in languages that require the new keywork in order to create an object/variable.
SOURCES
http://fredosaurus.com/notes-cpp/functions/refparams.html
you are tryng to call the "new" operator on a reference. while the "new" is used only with pointers.
pass to void foo( ) function
Person*
instead of
Person&
A little explanation :
The right way to pass a parameter depends on what you have to do!!
If you want to do side effects to the object passed to the function the right way is to declare :
void foo(Person& person)
the object person can be modified..
if you don t waant to do side effects to the object you have to declare the object 'const':
void foo(const Person& person)
the const means you cannot modify the object inside your method even if you are passing a reference..
then you can pass a pointer:
void foo(Person* person)
here you can modify the object that "person" is pointing at, but you have a copy of the original pointer.
last way to pass parameters is :
void foo(Person*& person)
here you have an alias of the original pointer. alias means "the same pointer with a different name"