Resizing an array of objects - c++

i'm trying to do a homework in c++ where I can't use STD container classes such as vector,map,etc. Suppose we have the following archive:
2
El juego de Ender-Orson Scott-Tor Books-980.275.246-1985-6000
890365.2
2
The following-Lily Tyler-Tor Books-980.275.246-1985-6000
890365.2
For explanation purposes lets assume the number 2 means that a book is from X category, in this case 2 means is a fiction book, the next line after 2 is just the basic book data (name,author,publishing house,isbn,year,price) and the next line after that is just something that we are going to call CCF that is just another code for these types of books from the fiction category.
I'm just stuck on the next following part, I do know how to create an array of objects without container classes, but I do not know how to resize this array, i've been hearing about realloc, but i don't know how to use it with object array types. Libro is the parent of Ficcion and i'm trying to store Ficcion types in a polymorphic array of Libro because later on I have to introduce more classes in the same array, but for learning purposes i'm trying to resize with just the ficcion class, that being said, I do know the next following code is wrong because i'm using malloc and allocating the child object with new and then reallocating, but as I said I don't really get how to do this at all cause if you see the code you can tell that a new child object is created every 3 times inFile has executed getline, if you think something's missing please let me know, but I think what i've exposed covers the main issues of this question. I'm just asking for some enlightment of information when you have to deal with these types of arrays and you need to resize them, and I also know this would be way more easier with the container class but I can't use it this time. Thanks in advance! have a great day/night!
void BookManager::dataLoading(char* fileName){
//Arbitrary size to be resized later on (this is the array declared as **libro in the .h file)
libros = (Libro**)malloc(1 * sizeof(Libro**));
//file-in variable
std::ifstream inFile(fileName);
char type[10];
//counter
int i = 0;
//First getline
while (inFile.getline(type,sizeof(inFile)) && i < 2){
//Book Info; Second Getline
char bookInfo[100];
inFile.getline(bookInfo, sizeof(inFile));
//Additional book information; Third getline
char additional[60];
inFile.getline(additional, sizeof(inFile));
//Child creation
if (type[0] == '2'){
this->separateData(bookInfo);
//Ficcion constructor creating an object to store in the libros polymorphic array
libros[i] = new Ficcion(this->bookData[0], this->bookData[1], this->bookData[2], atof(this->bookData[3]), atoi(this->bookData[4]), atoi(this->bookData[5]), atof(additional));
}
i++;
//Failed reallocating
libros = (Libro**)realloc(libros, i+1 * sizeof(Libro**));
}
// This one is just testing if the array worked but i get an unhandled exception when i=1;
for (int i = 0; i < 2; i++){
std::cout << libros[i]->getTitulo() << '\n';
}
inFile.close();
}
UPDATE solution:
Since this is an array of pointers, as suggested down below i created a function where it resizes it and deletes the old one, returning the new one with the old data and a free space to store. So far this is working, thanks a lot guys!
Libro** BookManager::resize(Libro** arr,int size){
Libro** resized = new Libro*[size];
for (int i = 0; i < size-1; i++){
resized[i] = arr[i];
}
delete[]arr;
return resized;
}

malloc, realloc, etc.. are C library functions. Your question carries the C++ tag, not C.
In C++, objects are allocated with new, and deallocated with delete. This includes arrays.
To resize an existing array, you use new to allocate a new array, with the new size, copy into it the elements in the existing array, then deallocate the old array with delete.
(This is not 100% technically true, but this is likely what you are expected to do as part of your assignment).

Related

C++ dynamic array of pointer to another class

Hello i'm trying to create a dynamic array of pointer to an object Student from Gradesclass but i can't figure out how to declare it in the header
that's the header:
class Grades
{
private:
Student** array;
int _numofStud;
public:
Grades();
Grades(const Grades& other);
~Grades();
and the grades constructor (i'm not sure it's right)
Grades::Grades()
{
this->array = new Student * [2];
for (int i = 0; i < 2; ++i)
{
this->array[i] = NULL;
}
this->array[0]= new Student("auto1", "12345");
this->array[1]= new Student("auto2", "67890");
this->_numofStud = 2;
}
The probleme is that before it even enter to the constructor, it creating me an array of Size 5 in Grades because i have 5 elements in the Student constructor
Student::Student(const char* name, char* id)
{
this->_numofgrade = 0;
this->setName(name);
this->setId(id);
this->_grades = NULL;
this->_average = 0;
}
And i can't add or modify this size
I want to put a default size of Grades to an array of 2 pointers to student object that i'll define as default then i'll have an other methods that add new Students by creating them and adding their pointers to the array
Th problem is i can't change the size of array and i don't understand why
I hope i was clear in my explanation thanks for your help
Edit:
that's the debuger and you can see when it's creating a new object Grades g1
it's creating an array of 5 instead off two
fill the 2 first as i asked for
and the 3 left i have no idea why they have been created and whats inside them
OK, so to be clear, in any actual programs you should use std::vector or other containers, they have a lot of features I ignored here (being templates, supporting move semantics, not requiring a default constructor, etc.), a lot of saftey (what if a constructor throws an exception? What if I do array.add(array[0])?), while still being pretty well optimised for general purpose usage.
And you should also really look at std::unique_ptr, manual new, delete, is generally asking for leaks and other mistakes, in C++ a manual "free" or "delete" of any resource is almost never needed.
Also note in C++ size_t is often used for sizes/lengths of objects and containers.
So the basic idea of a dynamic array is it changes it's size based on current requirements, so Grades() can just start off empty for example.
Grades::Grades()
: array(nullptr), _numofStud(0)
{}
Then when adding a new item, a new larger array is made, and all the existing items are copied (roughly what std::vector::push_back(x) does).
void Grades::addStudent(Student *student)
{
// make a larger array
Student **newArray = new Student*[_numofStud + 1];
// copy all the values
for (int i = 0; i < _numofStud; ++i)
newArray[i] = array[i]; // copy existing item
// new item
newArray[_numofStud] = student;
++_numofStud;
// get rid of old array
delete[] array;
// use new array
array = newArray;
}

How to use a dynamically resizing String Array?

I'm trying to use an array in C++ that changes in size. For some reason the size does not change, it only ever holds 1 string. The difficult part is that the user cannot input the number of courses they are going to add, instead the addCourse function is called until the user stops. A vector cannot be used (this is for a school assignment, and a resizing array is required). I'm stuck as to why the array only seems to hold one string, I would think it to hold the equivalent of numCourses strings. How would I go about resizing to hold multiple strings after each call to the function?
void Student::addCourse(string* courseName)
{
int x;
numCourses += 1;//increments number of courses
string newCourse = *courseName;
string* newCourses = new string[numCourses];//temporary array
for(x=0; x<numCourses - 1; x++)//fills temp array with the values of the old
{
newCourses[x] = courses[x];
}
newCourses[numCourses - 1] = newCourse;//adds extra value
delete[] courses;//removes original array
courses = newCourses;//sets the new course list
}
Edit: For those asking why a vector cannot be used because the point of the assignment is to actively avoid memory leak using the heap. Using an array like this forces intentional delete of stored values.
The comment should have answered your question: there is no way for the debugger to know that a pointer to a string is pointed to an array, nor does it know its bounds, because no such information is kept at runtime (a std::vector will show its whole contents in the debugger, in contrast).
Your method prototype should read:
void Student::addCourse(const string& courseName);
If you don't want to have a memory leak, declare a pointer to courses in your class:
private:
string* courses;
Allocate space for an array of strings in your constructor:
Student::Student()
{
courses = new String[5];
}
Then deallocate in the destructor:
Student::~Student()
{
delete[] courses;
}
This gives you room for up to 5 courses. If you need more you need to adjust the size of the array of strings at run time:
void Student::ExtendArray()
{
delete[] courses;
courses = new String[10];
}
Note this code is not exception safe, but will give you the basic idea.

Trying to dynamically grow a static array c++

So I have to dynamically grow a static array in this struct I've made (I COULD use vectors but it specially asks me to use a static array and copy the elements so please don't ask me to use vectors).
Problem is I get segmentation faults and I don't know why. When I create a new instance of my object it sets an array with a length (currently 5 to test). I then run this code when resizing the array but it isn't working.
if (used == (length-1)) {
length *= 2;
T* newArr = new T[length];
for (int i = 0; i < used; i++) {
newArr[i] = items[i];
cout << "Copied" << endl;
}
items = newArr;
}
So it seems to copy to addresses over to the new array OK and seems to only copy the appropriate number of items. Used determines how many items have been added whereas length holds the size of the entire array. Such that when I create a new array the length should be double how many are used. When I try and assign items to newArr I get a segmentation fault so I'm wondering if that code is correct or if it's crashing there. I;ve tried debugging it line by line but can't tell if it's breaking there or not.
Q: "I COULD use vectors but it specially asks me to use a static array and copy the elements so please don't ask me to use vectors..."
A: Then the question is fundamentally flawed ;) You SHOULD use vectors ;)
If you really want to amuse your instructor, I'd encourage you to usemalloc(), realloc() and free() instead of "new" and "delete" ;)
Meanwhile - you're probably getting a segmentation fault by overwriting "newArr[]". "newArr[]" might be too small if "used" is a lot bigger than length.
Just a guess - using a debugger would clearly show you exactly what's happening.
Q: What compiler and debugger are you using?
Do a
delete[] items;
before the reassignment

Why can't initial length be 1 in a dynamically-allocated array?

Let's say we start out with:
int *newArray = new int[1];
And then later have something like:
ifstream inputFile("File.txt");
Counter=0;
while (inputFile >> newValue)
{
newArray[Counter] = newValue;
Counter++
}
If I try to pull 100 lines from the text file, the program will eventually crash. However, if I had used
int *newArray = new int[100];
originally, it doesn't crash.
If it's dynamically allocating memory, why does it need an initial value more than 1? That makes no sense to me. Having to define any initial length beyond a small number such as 1 or 10 defeats the whole purpose of dynamic memory allocation...
EDIT: This is for school, we aren't allowed to use vectors yet.
The language will not "dynamically allocate memory" for you. It is your responsibility to allocate and reallocate your arrays so that their sizes are sufficient for your purposes.
The concept of "dynamic allocation" in C++ never meant that memory will somehow allocate itself automatically for you. The word "dynamic" in this context simply means that the parameters and lifetime of the new object are determined at run time (as opposed to compile time). The primary purpose of dynamic memory allocation is: 1) to manually control object's lifetime, 2) to specify array sizes at run-time, 3) to specify object types at run-time.
The second point is what allows you to do this
int n = ...; // <- some run-time value
int *array = new int[n];
which is not possible with non-dynamically allocated arrays.
In your example, you can allocate an array if size 1 initially. Ther's nothing wrong with it. But it is still your responsibility to allocate a new, bigger array, copy the data to the new array and free the old one once you need more space in your array.
In order to avoid all that hassle you should simply use a library-provided resizable container, like std::vector.
It's not dynamic in the sense that it can dynamically resize itself. It's dynamic in the sense that its size can be chosen dynamically at runtime, instead of compile time. One of the primary philosophies of C++ is that you don't pay for what you don't use. If dynamic arrays worked the way you are asking, that would require bounds checking, something I don't need, so I don't want to pay for it.
Anyway, the problem is solved with the standard library.
std::vector<int> vec;
...
while (inputFile >> newValue)
{
vec.push_back(newValue);
}
Isn't that much nicer? You don't even have to keep track of the size, because vector keeps track of it for you.
If you can't use vector, then you've got a lot of work ahead of you. The principle is essentially this. You keep 2 additional integer variables. One to indicate the number of values you are using in your array, and one to indicate the current capacity of your array. When you run out of room, you allocate more space. For example, here is a poor man's non-exception safe version of a vector:
int size = 0;
int capacity = 1;
int array = new int[capacity];
while (inputFile >> newValue)
{
if (size == capacity)
{
capacity *= 2;
int * newArray = new int[capacity];
for (int i=0; i<size; ++i)
newArray[i] = array[i];
delete [] array;
array = newArray;
}
array[size++] = newValue;
}
You're only creating space for one int but trying to store several, of course it crashes. Even if you created it with size 100 it'd still crash when you tried to save the 101'th value.
If you need an automatically resizing container check out std::vector.
#include <vector>
std::vector<int> data;
while (inputFile >> newValue)
{
data.push_back(newValue);
}
This will work until your process runs out of memory.

How to manage an array of pointers to objects?

I have a problem with an array of pointers to objects :(..
I need to generate a dynamic vector of object and then return it in
order to manipulate it in another class. In the code below there is
Event class that is abstract and CarArrival that inherits from it and
can be instantiated.
Inside the class that generate and fill the array I have this function:
Event** EventGenerator::getEvents() {
Event* cars[EVENTS];
for (int i=0; i<EVENTS; i++) {
cars[i] = new CarArrival(generator->getNextNumber(8,(float)sqrt(0.4)));
}
sort(cars, cars+(EVENTS), Event::cmp);
return cars;
}
I invoke this function in onther class in this way:
Event** cars = generator->getEvents();
for(int i=0; i<EVENTS; i++) {
cout << i <<":" << (*cars)[i]->getScheduleTime() << endl;
}
after the print of the first element i get "Segmentation Fault".
I have read some things online and I understand that I mistake since (*cars) evaluates to a
pointer to the first element of the array, in fact I can print the first element and not the other, but I cannot figure out how to access every element of the array in the second class.
How can I face this?
Thanks to all,
Alberto
I'd suggest that you use a std::vector<Event*> instead. You'll save a lot of pain this way. It takes care of all the nasty memory management in the background, and you can easily push any number of items into it. The best part in your case is, that you can simply return a vector which is not safe with a normal array.
Also your Event* cars[EVENTS]; is declared locally in you function. After you have finished it, it ceases to exist, which might cause your Segfault. You'd have to dynamically allocate the array with new, but still, try it with std::vector, see the documentation here.
EDIT: Sample Usage:
std::vector<Event*> EventGenerator::getEvents() {
std::vector<Event*> cars;
for (int i=0; i<EVENTS; i++) {
cars.push_back(new CarArrival(generator->getNextNumber(8,(float)sqrt(0.4))));
}
sort(cars.begin(), cars.end(), Event::cmp);
return cars;
}
std::vector<Event*> cars = generator->getEvents();
for(int i=0; i<cars.size(); i++) {
cout << i <<":" << (*cars)[i]->getScheduleTime() << endl;
}
I believe the cleanest way to handle a dynamic vector of pointers to dynamically allocated objects is to use a boost::ptr_vector. It handles everything you need, including allocation of the space to store the pointers, and deletion of those pointers afterwards.
Wouldn't be better to return vector<Event*> or vector<shared_ptr<Event>> instead of raw pointers? This way you would gain:
Automation of the memory management
Dynamic array with built-in length instead of fixed one
As mentionned by Constantinuis, you are returning the value of a pointer to a memory location that is only valid in the scope of the getEvents() function (it's allocated on the stack). You're bound to get a segfault next time.
You probably want to allocate the memory for this array in the heap (using 'new' if my C++'s isn't too rusty), and then you'll have to deal with freeing the memory later.
http://www.linuxtopia.org/online_books/programming_books/thinking_in_c++/Chapter13.html