4

I am not allowed to use Vectors specifically for this school assignment. Most of the answers I've found simply state "you should use vectors" as the most up-voted comment. While I appreciate and understand this, I'm simply restricted from using them for this assignment.

It is a C++ assignment with dynamic memory management as follows:

// property in header declaration
int numAnimals;
int capacity;
Animal** animals; 
void addAnimal(Animal *newAnimal);

// class implementation
capacity = 10;
numAnimals = 0;

animals = new Animal*[capacity];

void SampleClass::addAnimal(Animal *newAnimal)
{
  for (int i = 0; i < capacity; i++){
       if(animals[i]){
          // animal object already exists in array, move on
          i++;
       }else{
          animals[i] = newAnimal;
          numAnimals++;
          break;
       }
   }
}

animals is a pointer to a pointer, in this case a pointer to an array of pointers to object type Animal which have yet to be created.

With the 'addAnimal' function, what I'm trying to do is add an animal object to the array by looping through the array of pointers, and if there already exists an animal object, iterate to the next index. If there is no animal, then insert that animal into the array.

I'm getting an exception thrown "read access violation" when I attempt to access a member function of an animal object in the array.

My suspicion is because: if(animals[i]) probably isn't doing what I think it's doing, running it through the debugger I never hit the 'else' portion, so the array is still full of pointers not set to any objects when the method is complete. Therefore, when I try to access a member function, it's of an object that doesn't exist.

So, if my suspicions are correct, then what is the best way to insert a new object into an array of pointers in this fashion? They need to be pointers otherwise it automatically creates the array full of populated objects, which is not what I want.

I didn't post all of the code because I wanted to keep my problem brief, apologies I'm a new to C++ and stackoverflow in general. Yes, I know to delete[] to clear memory afterwards.

Any help is appreciated, thanks!

3
  • 1
    You already have numAnimals. Simply use it: animals[numAnimals] = newAnimal; Don't search. Commented Oct 20, 2017 at 10:39
  • 3
    if(animals[i]) is testing for a null pointer, but when you get the memory from new there is no guarantee that the pointers are null. Using numAnimals seems like a better idea anyway. Commented Oct 20, 2017 at 10:47
  • Thanks @manni66, fast quick response and was very obvious when you brought it up. sp2danny 's answer is also more what I was looking for although I agree your tip works for my use case. Commented Oct 20, 2017 at 19:03

3 Answers 3

6

Since in numAnimals you keep count of the current number of animal pointers in the array, you don't need the for loop to find the first available slot to add a new animal pointer (note also that, assuming you want to use a for loop like shown in your code, you have to pay attention to properly initialize all the initial pointers in the array to nullptr).

You can just use:

// Inside SampleClass::addAnimal(Animal *newAnimal):

animals[numAnimals] = newAnimal;
numAnimals++;

Please note that you have to pay attention to not overflow your array capacity, when you insert a new animal.
So, before inserting a new animal, you have to check that there's enough room in the array, e.g.:

// Before inserting:
if (numAnimals == capacity) 
{
    // You ran out of capacity.
    //
    // 1. Allocate a new array with bigger capacity (e.g. 2X)
    // 2. Copy the content from the current array to the new one
    // 3. delete current array
}

As a side note:

Yes, I know to delete[] to clear memory afterwards

Note that if you call delete[] on the animals array of pointers, you release this pointer array, but not the Animal objects pointed to.

Sign up to request clarification or add additional context in comments.

Comments

4
int capacity = 10;
Animal** animals = new Animal*[capacity];

This allocates ten pointers, except it does no initialization, meaning you have essentially garbage data.

if (animals[i])

This test one of those pointers against nullptr, but since you did no initialization, it's not very likely that it's nullptr.

You need to add a loop-pass that null out the data, right after you allocate it:

for(int i=0; i<capacity; ++i)
    animals[i] = nullptr;

There is also a bug:

if (animals[i]) {
    // animal object already exists in array, move on
    i++; // <- here

This is wrong, you move on twice for (int i = 0; i < capacity;i++)

Comments

2

The thing is "Use a vector", despite your teacher telling you otherwise, is the correct way to do it. However, if you are supposed to manage the dynamic array manually, then the best you can do is to encapsulate all that dirty memory stuff inside a class and write your own replacement for std::vector. Ie to avoid having dynamic allocations spread all over your code (especially in places that are supposed to deal with Animals and shouldnt care about manually allocating memory and the like) you should write a class that does that (and nothing else) and provides a nicer interface. I can only outline the idea here:

template <typename T> 
class my_vector {
    private:
        T* data;
        size_t size;
        size_t capacity;
    public:
        void push_back(const T& t);
        size_t size();
        T& operator[](size_t index);
        void resize(size_t size);
        //... etc...
};

2 Comments

"Use a std::vector ... is the correct way to do it" It depends what "it" is. If "it" is writing maintainable production code, then I agree. If "it" is experimenting with pointers so that you understand how dynamic memory allocation works, then std::vector is not much help.
@ArthurTacca thx for pointing it out, I actually meant "use a vector". Even for experimenting with pointers using a vector-like class is "the way" imho. If not allowed to use std::vector then imho one should write ones own replacement

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.