A very simple code with a weird issue. The code goes through fine but I can't seem to get the desired output. My getStock() and getQuantity() functions don't seem to work. When I debug the code, it says 'error reading the memory'. When the execution reaches s.dispP() the code crashes unexpectedly. Can't seem to find a solution for it. Kindly help. Thank you.
#include<iostream>
#include<conio.h>
using namespace std;
class Sale
{
class SaleItem
{
int stock, quantity;
public:
SaleItem(int pstock, int pquantity) : stock(pstock), quantity(pquantity)
{
}
int getStock()
{
return stock;
}
int getQuantity()
{
return quantity;
}
};
int sstock, squantity;
public:
SaleItem *si;
void addP()
{
cout << "Enter Stock: ";
cin >> sstock;
cout << "Enter Quantity: ";
cin >> squantity;
SaleItem *si = new SaleItem(sstock, squantity);
}
void dispP()
{
cout << si->getStock() << endl << si->getQuantity();
}
};
void main()
{
Sale s;
s.addP();
s.dispP();
_getch();
}
The error comes from the following method:
void addP() {
cout << "Enter Stock: ";
cin >> sstock;
cout << "Enter Quantity: ";
cin >> squantity;
SaleItem *si = new SaleItem(sstock, squantity);
}
Here the si is just a local variable and not the member variable you are thinking it is. To fix the issue, just prepend the si with a this-> or just use it without the this pointer.
void addP() {
cout << "Enter Stock: ";
cin >> sstock;
cout << "Enter Quantity: ";
cin >> squantity;
this->si = new SaleItem(sstock, squantity);
}
Alternative is to use a naming convention for member variables, e.g. prefix m_, _ or suffix _.
Although the correct modern C++ approach here is to not use raw pointers at all. Any memory you allocate with new must have delete called on it. And you have not called delete to deallocate the memory you allocated, and this causes a memory leak.
The modern C++ solution is to use std::unique_ptrs instead to automate memory management.
public:
std::unique_ptr<SaleItem> si;
void addP()
{
cout << "Enter Stock: ";
cin >> sstock;
cout << "Enter Quantity: ";
cin >> squantity;
this->si = std::make_unique<SaleItem>(sstock, squantity);
}
void dispP()
{
cout << si->getStock() << endl << si->getQuantity();
}
Note that you might not need to use smart pointers here at all. Simple objects might do. Have the knowledge of the options available at your disposal and use the best one :)
Here
SaleItem *si = new SaleItem(sstock, squantity);
you are not assigning the result of the new expression to the si field; instead, you created a local variable si (that shadows the field that has the same name) and initialized it with the result of the new expression.
The si field is remains uninitialized, and thus when you later try to use it you get a crash (actually, you are lucky, an uninitialized pointer may silently appear to work and overwrite unrelated memory).
To fix this, you have to change the new variable definition in an assignment; so, that line simply becomes
si = new SaleItem(sstock, squantity);
Notice that your class is leaking memory, as it calls new without a corresponding delete; the immediate fix here would be to use a smart pointer such as unique_ptr, but, unless you need a pointer for some other reason, here you should just have SaleItem as a "regular" (non-pointer) field inside Sale and forget about all memory management issues.
The line
SaleItem *si = new SaleItem(sstock, squantity);
introduces a local variable named si. It does not set the value of the member variable of the class. As a consequence, the member variable si remains uninitialized. Accessing such a variable causes undefined behavior.
You can use
si = new SaleItem(sstock, squantity);
to remove the particular problem you are facing but realize that your class is very fragile.
The member variables sstock and squantity seem to be intended for SaleItem but they are declared outside of that class. It's not clear whether that was from an error in copying and pasting code from you computer to the post, or the error exists on your computer too.
It's always a good idea to initialize all member variables of a class in the constructor. si can be initialized to nullptr in the constructor of the class.
You haven't shown why you need to use a pointer. If your class needs one object, use an object. If it needs a list of objects, use a std::vector of objects.
If, for some reason, you need to store a pointer in your class, you need to be aware of The Rule of Three and make sure to update your class accordingly.
You declared a local variable si in the addP() method which shadows the member variable si of the class Sale. Thereby the member variable si is not initialized and your subsequent reference to it causes a crash.
SaleItem *si;
void addP()
{
cout << "Enter Stock: ";
cin >> sstock;
cout << "Enter Quantity: ";
cin >> squantity;
SaleItem *si = new SaleItem(sstock, squantity);
}
At the last SaleItem *si = new SaleItem(sstock, squantity); you are creating another, function local SaleItem* si which holds the newly created object's address, and then it immediately goes out of scope.
Instead, you want your member si to be assigned with it, so another function can use it.
So, instead of declaring another si, just refer the member si.
SaleItem *si;
void addP()
{
cout << "Enter Stock: ";
cin >> sstock;
cout << "Enter Quantity: ";
cin >> squantity;
this->si = new SaleItem(sstock, squantity);
}
P.S.
If you are initializing the member si from within a function, and accessing it from yet another function, you can't be sure about the order of calls.
To save yourself from later headache because of initial garbage address in pointer si causing segfaults, I would suggest to have it initialized to nullptr :)
I am not very sure why you want to use si as a pointer to SaleItem?
Maybe I am missing something, but it doesn't seem to me necessary, unless you are trying to create a list of SaleItems (then I don't think you are implementing the class correctly, see below).
In any case, with that structure I would just go for
public:
SaleItem si(0,0);
void addP()
{
cout << "Enter Stock: ";
cin >> sstock;
si.stock=sstock
cout << "Enter Quantity: ";
cin >> squantity;
si.quantity=squantity;
}
void dispP()
{
cout << si.getStock() << endl << si.getQuantity();
}
However, if what you are trying to create is a class Sale that is a list of SaleItems then you need to approach things differently. In that case yes, you would have a pointer to an object SaleItem. I would understand that si is an array of elements of the type SaleItem. You then need to allocate memory for the array when you create it, which implies that you know beforehand the maximun number of elements this array will have. Then you can have an index and each time you add a new SaleItem to the array you do it according to this index, being careful not to go beyond the allocated maximum number of items.
If what you are looking to implement is a dynamic data structure that grows as you add elements, then depending on what particular one you choose to implement you will need to make use of pointers. For a example, you could have a pointer to the next SaleItem and a pointer to the previous one. Or alternatively, the pointer to the next SaleItem could be within the SaleItem definition. The point is that you will have to link them somewhere, somehow. If this is the case then I recommend you do some reading on data structures. You can try
http://www.cprogramming.com/algorithms-and-data-structures.html
https://www.tutorialspoint.com/cplusplus/cpp_data_structures.htm
https://www.tutorialspoint.com/data_structures_algorithms/data_structures_basics.htm
If that is what you are after, I hope that helps :)
Related
I am writing an inventory management system using dynamic arrays using objects. The strategy for this is NOT to use vectors, but allocate a dynamic array that 1 increment by 1 every time the client needs to add to the inventory. I am getting a "segmentation fault" error so I believe it is a memory leak.
I have tried rewriting it to match addresses but no luck. I think the buildItem function produces a temporary object, which is destroyed when the function ends. I don't know how to fix this though.
#include <iomanip>
#include <iostream>
#include <string>
using namespace std;
class InventoryObject {
private:
int itemNumber;
string description;
int qty;
float price;
public:
int getItemNum() { return itemNumber; }
string getDescription() { return description; }
int getQty() { return qty; }
float getPrice() { return price; }
void storeInfo(int p, string d, int q, float pr);
void showValues(InventoryObject &item);
};
// Function Implementation
void InventoryObject::storeInfo(int p, string d, int q, float pr) {
itemNumber = p;
description = d;
qty = q;
price = pr;
}
void InventoryObject::showValues(InventoryObject &item) {
cout << fixed << showpoint << setprecision(2) << endl;
cout << "Part Number : " << item.getItemNum() << endl;
cout << "Description : " << item.getDescription() << endl;
cout << "Quantity: : " << item.getQty() << endl;
cout << "Price : " << item.getPrice() << endl << endl;
}
// Function Prototypes for Client Program
InventoryObject buildItem();
void drawMenu();
void showValues(InventoryObject &);
void printInventory(int size);
int main() {
int size = 1;
int choice;
bool quit = false;
InventoryObject part;
InventoryObject *iArray = new InventoryObject[size];
drawMenu();
cin >> choice;
while (quit == false) {
if (choice == 1) {
InventoryObject item;
item = buildItem();
iArray[size] = item;
}
if (choice == 2) {
iArray[size].showValues(iArray[size]);
}
if (choice == 3) {
quit = true;
}
}
return 0;
}
// This function accepts the data from the client and creates a new
// InventoryObject object. The object is then supposed to be added to the
// dynamic array.
InventoryObject buildItem() {
InventoryObject *tempObject = new InventoryObject;
int itemNum;
string description;
int qty;
float price;
cout << "Enter data for the item you want to enter:\n\n";
cout << "Item Number: \n";
cin >> itemNum;
cout << "Description: \n";
cin.get();
getline(cin, description);
cout << "Quantity: \n";
cin >> qty;
cout << "Unit price: \n";
cin >> price;
tempObject->storeInfo(itemNum, description, qty, price);
return *tempObject;
}
void drawMenu() {
cout << "1. Add Inventory\n";
cout << "2. Display Inventory\n";
cout << "3. Quit Program\n";
}
I expect the object to be created, and put into the dynamic array. Then redraw the menu and interact with the client from there.
The main problem is that you are writing past the area of memory you allocated for your array (iArray).
Specifically, this line of code:
iArray[size] = item;
Should actually be:
iArray[size - 1] = item;
The above does not cause memory leak, but something else you do in your program does:
Your function buildItem does this when it returns the value of a pointer without first deleting the pointer.
To fix this, change
InventoryObject *tempObject = new InventoryObject;
to
InventoryObject tempObject;
And finally, remember to delete iArray before return 0; in main
delete[] iArray
Build your program with debug symbol (-g flag)
Run limit-c unlimited
Run your program until it crashes
Run gdb core where is the name of your program
In the gdb climate type bt.
You should now have a back trace of where your program is crashing
Daniel, here my observations:
The function buildItem must return a pointer of InventoryObject i mean: InventoryObject*. It is bease you are returning the pointer of the memory that was reserved at the moment of create it (InventoryObject *tempObject = new InventoryObject;), like this:
InventoryObject* buildItem() {
InventoryObject *tempObject = new InventoryObject;
//...
tempObject->storeInfo(itemNum, description, qty, price);
return *tempObject;
}
keep in mind that your main array (InventoryObject *iArray) contains an array of pointer.
The main problem that i see. In the main you are using an if block where there is a local variable called item (InventoryObject item). Let me explain something: Each instance of a class, like you InventoryObject item will be destroyed, release its memory, if its context is over.
So, in this case, the problem is that you was casting the pointer of at the moment of returning from the function buildItem (return *tempObject) and store its value into a local variable InventoryObject item which shall be release when the thread leaves the if, the context of the local variable.
Change your local variable InventoryObject item to handle a pointer: InventoryObject* item
if (choice == 1) {
InventoryObject* item;
item = buildItem();
iArray[size] = item;
}
I recommend you to read the chapters 7. Pointers, Arrays, and References and 17 Construction, Cleanup, Copy, and Move from the book The C++ Programming Language of Bjarne Stroustrup.
I have tried rewriting it to match addresses but no luck. I think the buildItem function produces a temporary object, which is destroyed when the function ends.
Your intuition is half true. There is something wrong with this function, but it has nothing to do with temporaries. It's more about you allocating an object you never destroy.
The problem is that you are trying to do something similar to what I was trying to do in this question, but not quite. Even if you were doing what I was doing, it still wouldn't be a good idea for reasons that lots of people already explained better than I could.
The point is is that the base of the issue is that is if you use new to allocate something, you must use delete to deallocate it, which you don't do here.
InventoryObject buildItem() {
InventoryObject *tempObject = new InventoryObject;
//...
return *tempObject;
}
You simply return the object by value without deleting it first. You aren't even returning it by reference which would be better (yet still probably bad: again, see my question). You are allocating an object and never deleting it. That's a leak.
You have two solutions: either return the pointer directly (if you really need a pointer) like this:
InventoryObject* buildItem() {
InventoryObject *tempObject = new InventoryObject;
//...
return tempObject;
}
Or, you can simply just return the object by value like this:
InventoryObject buildItem() {
InventoryObject tempObject;
//...
return tempObject;
}
Given the code sample you've shown, this is my recommendation of the two.
Note: I should mention that if for some reason you do need to return a pointer and have some factory method pattern with some sort of polymorphism in play, you also have the option of smart pointers too. That is what I ended up doing based on other people's suggestions from the other thread. I can personally recommend it. Basically, your code code would become something like this instead:
std::unique_ptr<InventoryObject> buildItem() {
std::unique_ptr<InventoryObject> tempObject = new InventoryObject; // C++11 way, I think
// C++14 and above has a function called make_unique you could use instead.
//...
return tempObject;
}
However, this is only useful if you have some sort of polymorphism going on as I did. Given the code sample you have posted so far, you really only need to return by value in your situation. I just thought I'd mention this other option too, just in case.
P. (P.) S. This answer is only about your suspected memory leak. That said, there are other helpful answers here that you should heed. Problems described in other answers could contribute to your problem and could really trip you up down the road (actually, could still trip you up now too) if you don't fix them, so I highly recommend taking their advice too.
I'm trying to make a class for the player, then the object and the idea is to make the bottom cin save what he writes in the variable "name", but when executing it says that the variable "intr" has not been declared.
class jugador{
public:
string nombreIntr(string intr);
private:
string nombre;
};
string jugador::nombreIntr(string intr){
nombre = intr;
return nombre;
}
main(){
jugador jugadorActual();
//G
cout << "G: Bienvenido Jugador!\n\nG: Me dirias tu nombre?\n\n";
//Introducir Nombre
cin >> jugadorActual.nombreIntr(intr);
}
cin >> jugadorActual.nombreIntr(intr);
This says to take data from cin and put it into whatever nombreIntr returns. Since nombreIntr returns a temporary object (a copy of the nombre member), this is rather ineffective (even if it had compiled).
The simplest approach to what you want to do is probably to break this statement into its two conceptual parts: get a value from cin and store that value in jugadorActual.
cout << "G: Bienvenido Jugador!\n\nG: Me dirias tu nombre?\n\n";
std::string nombre;
cin >> nombre; // Get the value.
jugadorActual.nombreIntr(nombre); // Store the value.
There are ways to combine this into one step, but the ways I'm thinking of would require expanding the jugador interface, so not as simple as the above.
I have problem with adding object to array of pointers of objects. Here's a function I'll be talking about:
void add(Car **cars, int number) {
Car *fresh = new Car;
fresh = new Car;
cout << "Enter the name of your car." << endl;
cin >> fresh->name;
cout << "Enter max velocity of your car." << endl;
cin >> fresh->maxV;
cout << "Enter weight of your car." << endl;
cin >> fresh->weight;
delete[number-1] cars;
cars[number-1] = fresh; // here's something wrong
}
I allocated memory for number-of cars in array of pointers **cars and then I try to add new object fresh at the end of array, firstly deleting memory in last index of array of pointers of objects and then passing reference to last index of array, but I get an error. I tried to solve problem myself, by I didn't found similiar topic in web. I hope somebody will guide me how to solve it. Thanks for help.
No need of delete statement. The code would be like this if you already allocated memory for 'cars'.
Car *fresh = new Car;
cars[number-1] = fresh; //number here should be index starting from 1 not number of Car objects
The cars would be initialised like following before calling method add().
Car ** cars = new (Car *)[number];
if you need to delete the allocated memory for cars[number-1] use
delete cars[number-1];
Otherwise, you don't need to call new twice to allocate fresh.
So, the following should work
void add(Car **cars, int number) {
Car *fresh = new Car;
cout << "Enter the name of your car." << endl;
cin >> fresh->name;
delete cars[number-1];
cars[number-1] = fresh;
}
and you need to make sure that number is the size of your cars array.
It could be better if you use
std::vector<Car> cars;
So you could do the next:
cars.push_back(fresh);
And that's all. Sorry about my english
I am working on an assignment where we have to create a "student" object with a "course" member array that is dynamic so the user can enter from one course to as many as they'd like in the array. I've been trying this every which way and just cannot get the array to resize and accept new elements. Here is my code as it stands now:
cout << "Enter student name > ";
cin >> name;
Student firstStudent(name);
cout << endl << "Enter \"done\" when you are complete";
cout << endl << "Enter a new course : ";
cin >> course;
while (course != "done") {
numCourses++;
firstStudent.addElement(numCourses, course);//makes the array bigger
cout << endl << "Enter a new course : ";
cin >> course;
}//end while
member variables and constructor:
class Student
{
private:
string name;//name of student
string *dynArray = new string[1];//array
public:
Student::Student(string name)
{
this->name = name;
this->numC = 1;//initialized to one to start with
}
resizing and adding an element to the array:
void Student::addElement(int numCourses, string &course) {//DYNAMIC ARRAY
if (numCourses > this->numC) {//checks if there are more courses than there is room in the array
this->numC = numCourses; //this changes the member variable amount
dynArray[numCourses] = course;//adds new course to array
string *temp = new string[numC];//create bigger temp array
temp = dynArray;//write original to temp
delete[]dynArray; //this tells it that it's an array
dynArray = temp;//make dynarray point to temp
}
else {
dynArray[numCourses] = course;//adds new course to array
}
}//end addElement
Even if I manage to get rid of compile errors, it will often come up with runtime errors. I'm sure it has to do with pointers/copying arrays but I'm just learning and haven't yet mastered these concepts.
Edit: dynArray[numCourses] = course; creates a char array. ie if course = "one", dynArray[0] = "o", dynArray[ 1] = "n", etc. Does anyone know how to keep this from happening?
screenshot
The first line that that doesn't look quite correct is this one:
dynArray[numCourses] = course;
Before it is checked that numCurses > numC, which means that dynArray[numCourses] accesses memory out of bounds (the bounds are 0 to numC - 1.
The other thing that strikes me as interesting is that the addElement method takes numCourses as an argument. I would expect that it should behave similar to std::vector<T>::push_back. Are you sure this argument is necessary?
The last thing I want to comment on is that the values from dynArray are not copied. Have a look at std::copy_n on how to do the copy.
I'm not sure if this would be considered cheating, but here's a break down of how std::vector is implemented. You could use that as a reference and just pick out the parts you need.
http://codefreakr.com/how-is-c-stl-implemented-internally/
Is it dangerous to have cin input directly assigned to a double variable that is passed by reference? If so, what measures can I take to defend against dangerous input - aside from not passing by reference.
Example below:
void samplefunction(double &var1, double &var2)
{
cout << "Enter something: ";
cin >> var1;
cout << endl;
cout << "Enter something: ";
cin >> var2;
}
The short answer, is no, as the cin >> operator will only read as much data as it requires to fill the type. The rest is discarded until the next whitespace. You may not get correct values, for example if someone feeds in "ABCDEF" instead of "1.0," but you won't have to worry about a buffer overflow.