Adding object to array of pointers to objects - c++

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

Related

How do I instantiate objects and have them still be available outside of their scope? (e.g. instantiated an object in a loop)

I'm trying to write a program that takes in user input and uses it to create objects that are then pushed to a list. Here is a simplified version of what I wrote:
int main(){
cout << "How many lists? \n";
int numLists;
cin >> numLists;
list<MyObject> objectList;
for(int i = 0; i < numLists; i++){
cout << "Enter some input for list #" << i << "! \n";
int n = 3;
while(n-- != 0){
int input;
cin >> input;
MyObject object(input);
objectList.push_back(object);
}
//Save the newly created list somewhere
}
//Do something with all the created lists
}
Once the loop finishes, the objects created inside of the loop end up out of scope and the list has size zero.
I started using
list<MyObject> *list = new list<MyObject>
...
MyObject *object = new MyObject(input);
...
(*list).push_back(*object);
and it seems to be working fine, but I was wondering if there was a better way to achieve this. Furthermore, since I am using multiple instances of list and MyObject outside of the loops from which they were created, how do I clear the memory?
Thank you
Read up on scope for C++ here:
https://en.cppreference.com/w/cpp/language/scope
Why do you want to reference objects made in the for loop? They have already been copied into the list<> object, so you can read/modify them there.

Increasing class pointer array

In my code, I want to add one student info into my class pointer array and the array size will increase each time a new student is added. Here is my code:
My header file:
class Student{
public:
int studentID;
char studentName[20];
int currentEnrollment;
Student();
void AddStudent(Student *tempStudent[], int countStudent, int sizeOfArray);}
My Class definition file:
void Student::AddStudent(Student *tempStudent[], int countStudent, int sizeOfArray)
{
for (int i = countStudent; i < sizeOfArray; i++)
{
cout << "Please enter student id (4 digits only): ";
cin >> tempStudent[i]->studentID;
cout << "Please enter student name: ";
cin >> tempStudent[i]->studentName;
}
}
My Main.cpp file
int *totalStudent = new int;
*totalStudent = 1;
int i, j, countStudent = 0;
int sizeOfArray = *totalStudent;
Student *newStudent[*totalStudent];
//Each time a new student is added, I will allocate a new memory for the array element, then add student Info using function.
for (i = countStudent; i < *totalStudent; i++)
{
newStudent[i] = new Student;
newStudent[i]->AddStudent(newStudent, countStudent, sizeOfArray);
countStudent++;
*totalStudent++;
}
When I run my code, I get an undefined reference error, so I do not know If I am able to increase my array or not. I intend to use C++ syntax so I use new and delete. Thank you for your help.
P.S: Here is my new code and it runs great, the only missing is the studentID for the first element in array.
In my main class:
int numStudent = 0;
int i, j, countStudent = 1;
Student *newStudent = new Student[countStudent];
AddStudent(newStudent, countStudent, numStudent);
My Student.h
class Student{
public:
int studentID;
char studentName[20];
int currentEnrollment;
};
Student AddStudent(Student *newStudent, int &countStudent, int &numStudent);
and My Student.cpp
Student AddStudent(Student *newStudent, int &countStudent, int &numStudent)
{
Student tempStudent;
cout << "Please enter student id (4 digits only): ";
cin >> tempStudent.studentID;
cout << "Please enter student name: ";
cin >> tempStudent.studentName;
newStudent[numStudent] = tempStudent;
numStudent++;
if (numStudent == countStudent)
{
Student *newStudentSize = new Student[countStudent + 1];
for (int i = 0; i < numStudent; i++)
{
newStudentSize[i] = newStudent[i];
}
delete []newStudent;
return *newStudentSize;
countStudent += 1;
}
}
Running this code will give me the following result:
StudentID: 11
StudentName: Dat
StudentID: 23
StudentName: Michael
Printing:
StudentID: 0
StudentName: Dat
StudentID: 23
StudentName: Michael
While it doesn't make sense to increase the array for each new student (it's inefficient) here's one way you can do it (I didn't even try to make your code compile because it has a number of issues and is unnecessarily complicated). Note that tempStudent (in the code snippet below) doesn't even have to be created using new. This solution stores Student objects in the students array (although it's easy to modify it to store Student object pointers instead). That said, usually, you'll just want to create an array of large enough size to accomodate all students (just set studentCount to some appropriate number and not 1 like in the example below).
class Student{
public:
int studentID;
char studentName[20];
int currentEnrollment;
Student(){};
};
int main(){
int studentCount=1;
Student * students = new Student[studentCount];
int numStudents=0;
bool done=false;
char finished='N';
while (!done){
//Student *tempStudent = new Student();
//create a Student on the stack
Student tempStudent;
cout << "Please enter student id (4 digits only): ";
//No input checking is done here
cin >> tempStudent.studentID;
No input checking is done here
cout << "Please enter student name: ";
cin >> tempStudent.studentName;
students[numStudents] = tempStudent;
numStudents++;
cout << "Stop entering students: Y or N";
cin >> finished;
done = finished=='Y' or finished=='y' ? true : false;
if(numStudents==studentCount){
students = ReallocateStudents(students, studentCount, studentCount*2);
studentCount *= 2;
}
}//end while
//print the students info
for(int i=0;i<numStudents;i++){
Student st = students[i];
cout << st.studentID << " " << st.studentName << std::endl;
}
//deallocate the students array or if you place this in the main like you did, the program will exit immediately so there's no need to deallocate
return 0;
}
Student * ReallocateStudents(Student* st, int oldSize, int newSize){
Student * newStudents = new Student[newSize];
//copy the existing values from the old array to the new one
for(int i=0;i<oldSize;i++){
newStudents[i] = st[i];
}
delete [] st; //delete the old array
return newStudents;
}
UPDATE:
Since you don't want to do everthing in the main(), just create a free AddStudents function and do everything there. Alternatively, you can create a
static function inside the Student class. It makes no sense to create AddStudent as a member of Student because that would require you to use an instance of Student to add a new instance, which makes for poor design (not to mention technical issues etc).
int main(){
// some code here
Students * allStudents = AddStudents();
//print students
}//end main
Students * AddStudents(){
int studentCount=1;
Student * students = new Student[studentCount];
int numStudents=0;
bool done=false;
char finished='N';
while (!done){
//create a Student on the stack
Student tempStudent;
cout << "Please enter student id (4 digits only): ";
//No input checking is done here
cin >> tempStudent.studentID;
No input checking is done here
cout << "Please enter student name: ";
cin >> tempStudent.studentName;
students[numStudents] = tempStudent;
numStudents++;
cout << "Stop entering students: Y or N";
cin >> finished;
done = finished=='Y' or finished=='y' ? true : false;
if(numStudents==studentCount){
students = ReallocateStudents(students, studentCount,
studentCount*2);
studentCount *= 2;
}
}//end while
return students;
}
This is simple and easy to both understand and maintain so I suggest using this approach.
addStudent does not do anything with the Student object it belongs to. So there is no need to put it in the 'Student' class. (Or you could rather rewrite it so it does something with the student object it belongs to). And it currently does not "add" anything, so the name is confusing.
What is technically wrong with it depends on what you want it to do. Currently it initializes student objects expected to already exist and pointed to by an array, from a specific array index, to the end of the array. That could well be a useful function, if that is what you want it to do. But you then must call it correctly with an array of pointers that point to valid Student objects, which you currently do not.
Currently in main you have a loop that initializes pointers in an array. And each time you initialize a pointer, you call AddStudent(..). The problem is that 'AddStudent()' tries to initialize ALL the student pointed to by your array.
This has two major problems (In addition to all the other problems with your loop).
Each time you create a new student, all your existing students will be
initialized again with new input from std::cin. (So for n students, you will
try to do n*n initializations)
While the loop in main is running, not all pointers in your array points
to existing Student objects. This may result in important data being
overwritten, a program crash or something totally different and unexpected.
You should sit back and reevaluate how you want to do things. Trying to fix single bugs in your existing code, one after another, will just create more bugs.
Just a hint to get you started:
class Student
{
public:
int studentID;
char studentName[20];
int currentEnrollment;
Student();
void init_from_cin();
};
And in your class definition file:
void Student::init_from_cin()
{
cout << "Please enter student id (4 digits only): ";
cin >> studentID;
cout << "Please enter student name: ";
cin >> studentName;
}
If you create a new Student like this:
Student *new_student = new Student;
new_student->init_from_cin();
Then after calling init_from_cin(), the Student object pointed to by new_student should be initialized.
How to create and initialize multiple Student objects in a loop, is left as exercise for the reader. But when you do it, you should understand what your lower and upper bounds of your loop are supposed to be. And you should also understand why moving the upper bound further away while your loop is running is a bad idea.
And never forget that sane programming languages start array indexing with 0.

Memory management issue in c++

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 :)

Dynamic string array without using vector

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/

Trying to figure out the mistake I made with pointers (C++)

I'm a newbie to C++, currently working on a project on dynamic allocation in my C++ class. I can't for the life of me figure out where I went wrong with pointers in this program. I have three different .cpp files that I'm working with, we're supposed to make a rudimentary playlist program.
Please don't give me tips on any of the other messy code I've got going, I'm simply trying to figure out the pointers:
(Playlist class, contains a dynamic array of songs)
Playlist::Playlist (int s) {
size = s;
list = new Song[size];
}
Playlist::~Playlist() {
delete [] list;
}
void Playlist::Add(Song * s) {
size++;
Song * newlist = new Song[size];
for(int i = 0; i < size - 1; i++) {
newlist[i] = list[i];
}
delete [] list;
newlist[size] = *s;
list = newlist;
}
(Menu program)
switch (choice) {
case 'A': {
cout << "\nEnter a song title to add: ";
cin.getline(tempTitle,36);
cout << "Enter the artist's name: ";
cin.getline(tempArtist,20);
cout << "Enter the category of the song ([P]op, [R]ock, [A]lternative, [C]ountry, [H]ip Hop, or Parod[Y$
cin >> catStorage;
tempCat = StyleChoice(catStorage);
cout << "Enter the size of the song in kilobytes: ";
cin >> tempSize;
Song * tempS = new Song;
tempS->Set(tempTitle, tempArtist, tempCat, tempSize);
mainlist.Add(tempS);
delete tempS;
break;
}
I get an error whenever I run the Add() function and then exit the menu program (I have an "X" condition that ends the while loop that keeps the menu going). Thanks! Let me know if you need more information on what any of the functions do.
Change:
newlist[size] = *s;
To:
newlist[size-1] = *s;
There are two possible issues:
Your assignment is always out of bounds. To access the last element of the array, you'll always have to use newlist[size - 1] rather than newlist[size] since the very first element is always at index 0 in C/C++.
Since you don't verify that you've got an old list before trying to delete it with delete [] list; you'll have to make sure that your pointer is initially set to NULL or there is some initial (valid) pointer assigned to it that indeed can be deleted.