Dynamic arrays?

Now I tried to resize the array:

void addFood(Food* newFood, size_t foodItemsCount)
{
    Food* newFoodArray[foodItemsCount + 1];

    for (int i = 0; i < foodItemsCount; i++)
    {
        newFoodArray[0] = food[0];
    }

    newFoodArray[foodItemsCount + 1] = newFood;
    delete[] food;
    food = newFoodArray;
}

But it gives me the error:
Incompatible types in assignment of ‘Food* [(((ssizetype)foodItemsCount) + 1)]’ to ‘Food* [1]’|

1 Like

If you want to resize the array then you’re going to have to use dynamic allocation.

If you’re going to be doing that a lot, it would be best to stick to using a std::vector, it’ll save you having to write all the resizing code.

There’s a reference for std::vector here:
http://en.cppreference.com/w/cpp/container/vector

You’ll need to #include <vector> to use it.

If you’re used to C# lists, essentially:

  • List<T> thing = List<T>(); -> std::vector<T> thing = std::vector<T>()
  • list.add(item) -> vector.push_back(item)
  • list[index] -> vector[index]
  • list.Count -> vector.size()
  • list.Capacity -> vector.capacity()
  • list.Clear() -> vector.clear()
foreach(var item in list)
{
  // stuff with item
}

->

for(auto & item : vector)
{
  // stuff with item
}

Removing stuff at a given index is a bit more complicated unfortunately.

list.removeAt(index) ->

auto iter = vector.begin();
std::advance(iter, index);
vector.erase(iter);

(std::advance requires #include <iterator>)

But you can make a helper for that:

template< typename T >
void removeAt(std::vector<T> & vector, size_t index)
{
  auto iter = vector.begin();
  std::advance(iter, index);
  vector.erase(iter);
}

Which can be used as:

removeAt(vector, index);
2 Likes

EDIT: I used vector now.

Okay thats useful. But how is it possible to do that wit pointers? I know I need a pointer to an array of pointers. Here my code:

Food** foodP;

void addFood(Food* food, size_t foodItemsCount);

void setup()
{
    Food* food[1];
    foodP = food;

    // Add without function
    Apple *apple = new Apple();
    foodP[0] = apple;

    // Add with function
    Apple *apple2 = new Apple();
    apple2->name = "Apple2";
    addFood(apple2, sizeof(foodP)/sizeof(foodP[0]));
}

void addFood(Food* newFood, size_t foodItemsCount)
{
    Food* newFoodArray[foodItemsCount + 1];

    for (int i = 0; i < foodItemsCount; i++)
    {
        newFoodArray[0] = foodP[0];
    }

    newFoodArray[foodItemsCount + 1] = newFood;
    delete[] foodP;
    foodP = newFoodArray;
}

int displayFoodMenu(foodType type, size_t foodItemsCount)
{
    uint8_t menuPosition = 0;
    bool leftArrow = false;
    bool rightArrow = false;

    if (foodItemsCount > 1)
    {
        rightArrow = true;
        leftArrow = true;
    }

    // Clear background and draw menu window.
    game.display.drawBitmap(0, 14, background);
    game.display.drawBitmap(1, 16, menu_window);
    game.display.setCursor(55 - ((5*(foodP[menuPosition]->name.size()))/2), 20);
    game.display.print(foodP[menuPosition]->name.c_str());
    foodP[menuPosition]->draw(50, 46, 1);

    if (rightArrow == true && leftArrow == true)
    {
        game.display.drawBitmap(99, 46, arrow_right);
        game.display.drawBitmap(4, 46, arrow_left);
    }

    while (true)
    {
        uint8_t oldPosition = menuPosition;

        if (game.update())
        {
            if(game.buttons.pressed(BTN_RIGHT))
            {
                if (menuPosition + 1 > foodItemsCount - 1)
                {
                    menuPosition = 0;
                }
                else
                {
                    menuPosition++;
                }
            }
            else if (game.buttons.pressed(BTN_LEFT))
            {
                if (menuPosition - 1 < 0)
                {
                    menuPosition = foodItemsCount - 1;
                }
                else
                {
                    menuPosition--;
                }
            }
            else if (game.buttons.pressed(BTN_B))
            {
                break;
            }

            if (menuPosition != oldPosition)
            {
                game.display.drawBitmap(1, 16, menu_window);
                game.display.setCursor(55 - ((5*(foodP[menuPosition]->name.size()))/2), 20);
                game.display.print(foodP[menuPosition]->name.c_str());
                foodP[menuPosition]->draw(50, 46, 1);

                if (rightArrow == true)
                {
                    game.display.drawBitmap(99, 46, arrow_right);
                }

                if (leftArrow == true)
                {
                    game.display.drawBitmap(4, 46, arrow_left);
                }
            }
        }
    }
}

The problem is that my displayFoodMenu hangs and freeze the simulator. :confused:

You can have a vector of pretty much anything, including a vector of pretty much anything.

That includes pointers !

std::vector<Item*> or std::vector<std::shared_ptr<Item>> or std::vector<unsigned> or even std::vector<std::vector<Item*>>

Some stuff can’t be std::vector'd though, because it requires copying/moving your objects inside the memory it manages (may be best to refer to the doc for that subject). That’ll never be the case for raw pointers!

I meant to move this earlier but forgot.


The reason your code fails is because you’re making the array pointers point to arrays allocated on the stack instead of on the heap, so calling delete[] ends up destroying part of the stack, which in turn upsets the whole program and makes it impossible to continue.

Also your for loop that’s supposed to do the copying won’t work because of the zeros


I’m glad you decided to go with std::vector instead.

Managing dynamically allocated arrays is far from straightforward.
There are so many edge cases that by the time you’re doing it right you’ve ended up rewriting a large chunk of the interior of std::vector, and thus might as well have used it in the first place.

If you want I’ll write an example of ‘doing it right’,
but it would be a very large example,
and my advice would still be “don’t use this, std::vector does the same, but better”.


I’m guessing by ‘some stuff’ you mean objects that have their copy and move constructors explicitly deleted or marked private or `protected?

2 Likes

Thanks, don’t need that anymore. Vectors seem to be pretty nice ^^
But I have a general question:
I use the food vector with pointers as global variable. Do I really need to save pointers in the vector? Since the vector is global its never out of scope.

I’m not entirely sure what you’re thinking,
but if you mean what I think you mean then I think your best bet would be to use a std::vector<std::shared_ptr<Food>>.

It’s slightly more expensive than some other alternatives but it will behave more closely to a C# List<Food> and it means you won’t have any memory leaks or invalid pointers hangining around.

3 Likes

Why not simply this?
vector.erase( vector.begin() + index )

1 Like

That would work too, as long as index is implicitly convertible to std::iterator_traits<typename std::vector<T>::iterator>::difference_type.

So removeAt could be defined as:

template< typename T >
void removeAt(std::vector<T> & vector, size_t index)
{
  vector.erase(vector.begin() + index);
}

std::advance works on other container types where begin() + index wouldn’t.


To be truely generic removeAt ought to be defined as:

template< typename Container, typename Difference =  std::iterator_traits<typename Container::iterator>::difference_type>
void removeAt(Container & container, Difference index)
{
  auto iter = container.begin();
  std::advance(iter, index);
  container.erase(iter);
}

Which would then work on containers other than std::vector, such as std::list.

2 Likes

Yeah but why I cant simply store instances of classes in the Vector?

You can.
You can do this:

std::vector<int> vec;

But from what I understand you want to use Food as a parent class and access virtual methods.

In which case, that would make this invalid:

std::vector<Food> foodList;
foodList[i] = Apple(); // Error, can't assign an object of type Apple to expression of type Food

Whereas this would be valid:

std::vector<std::shared_ptr<Food>> foodList;
foodList[i] = make_shared<Apple>(); // Can assign because shared_ptr contains a pointer

Or this (but don’t do this, because it makes deciding when to delete hard):

std::vector<Food *> foodList;
foodList[i] = new Apple(); // Can assign because it's a pointer

Additionally, virtual methods only work from pointers or references.

1 Like

Weird. Whats the logic behind that I can’t add child instances to a vector which use food as paren class? In C# that works.

That’s because in C# all classes are references.

In C#:

class Test { public int value; }

void test()
{
  Test a = new Test();
  a.value = 5;
  Test b = a; // b now refers to a
  b.value = 10;
  // a.value is now also 10
}

But in C++:

class Test { public: int value; };

void test()
{
  Test a = Test();
  a.value = 5;
  Test b = a; // b is a copy of a
  b.value = 10;
  // a.value is still 5
}

To get the same behaviour you have to use other techniques, for example:

class Test { public: int value; };

void test()
{
  Test a = Test();
  a.value = 5;
  Test & b = a; // b is a reference to a
  b.value = 10;
  // a.value is now 10
}

In C#, pretty much all class creation is done on the heap,
whereas C++ lets you choose where your class is allocated: heap, stack or the global variable area.

By default C++ classes are closer to how C#'s structs work.


Furthermore, child classes sometimes have extra member variables.
Consider this case:

class Parent
{
  char a;
}:

class Child : public Parent
{
 char b;
};

sizof(Parent) == 1) and sizeof(Child) == 2.

So if you tried to do:

Parent parent = Child();

You’re trying to fit 2 bytes into a 1 byte sized variable.
It just doesn’t work.

2 Likes

@Pharap Your grasp of the subject of C++ and ability to explain in plain language is something I have never seen before. I am not saying this to flatter you, I mean it.

You should write a book.

2 Likes

Wow, thank you.
Whether intentional or not I’m flattered all the same.
I get a lot of ‘thanks for the help’, but rarely do I get praise like that.

I have started writing books once or twice but I find it hard to keep going and to keep the tone right (one attempt was called “C++ for pudding brains” and was very tongue-in-cheek :P).

Truthfully I find it easier to explain 1-to-1 becuase I can adapt my explanation to suit the person I’m talking do.
For example, I know @zer0 has C# experience, so in his case I can use C# as a base line to relate the features to.

(Maybe one day when I’ve contributed enough I’ll be worthy of a ‘resident C++ expert’ or ‘resident C++ wizard’ badge.)

1 Like

Yeah I would buy that book xD :grin:

1 Like