C++ Reference Help

Hi All,

I am a C++ hack and are obviously doing something seriously wrong. I know this is a pointer / reference / value problem but I am totally confused. All the code is in my repository at https://github.com/filmote/Cars

I have a class called Car.h / Car.cpp. In the declaration I have the following (simplified down) …

class Car {

  public:
    Car(String name);
     void debug();
     void scroll(byte pixels);
     int getX();
     void setX(int value);
     void setCars(Car* value);
     
   private:
     int _x;    
     String _name;          
     Car* _cars;

};

… and in the implementation:

Car::Car(String name) {

  _name = name;

}

void Car::debug() {
  
  Serial.print(this->getName());
  Serial.print(": x=");
  Serial.print(this->getX());
  
}

void Car::scroll(byte pixels) {
  ...
  for (int i = 0; i < 3; ++i) {

    Car car = _cars[i];

    if (car != *this) {

      if (_arduboy.collide(car.getRect(), this->getRect())) {
    
        Serial.println("- Collide ----------------------------");
        car.debug();
        this->debug();

      }
    }

  }
  ...
}

int Car::getX() {
  return _x / 10;
}

void Car::setX(int value) {
  _x = value * 10;
  _renderRequired = true;
}

In my sketch, I initialise 3 Cars like this:

Car car1 = Car("Car_01");
Car car2 = Car("Car_02");
Car car3 = Car("Car_03");

const Car cars[] = { car1, car2, car3 };

void setup() {
  
  arduboy.begin();
  
  car1.setCars(cars);
  car2.setCars(cars);
  car3.setCars(cars);

  ...

Still with me?

When I run the code, I get the following output:

Collide ----------------------------
Car_01: x=0, y=0, width=12, height=148, xMax=12, yMax=148, bitmap=137
Car_03: x=-20, y=16, width=22, height=16, xMax=2, yMax=32, bitmap=103

What it is showing me is that the debugging code that is called from the two lines, car.debug(); and this->debug(); are producing different results. The second line is correctand the first line is in error. I would simply say the reference I retrieve from the array is wrong except it somehow manages to resolve the car’s name correctly which suggests that the reference is right.

I am totally lost and would love someone to school me on this.

Thanks in advance.

1 Like

It took me a while but completely ignoring all the other issues the crux of the problem is this:

The cars in the cars array are not actually car1, car2 and car3 like you’re expecting. They’re copies.

The reason the names are correct but everything else is wrong is because they’re copying car1-car3 just after they’ve been initialised by their constructor, but before you’ve called all the setX, setY etc.

The simplest way to fix this is to make cars an array of Car* (and change Car accordingly).

Which is to say:

In Cars.ino:

  • const Car cars[] = { car1, car2, car3 }; becomes Car * cars[] = { &car1, &car2, &car3 };

In Car.h:

  • Car* _cars; becomes Car ** _cars;
  • void setCars(Car * value); becomes void setCars(Car ** value);

In Car.cpp::

  • void Car::setCars(Car value[]) becomes void Car::setCars(Car ** value) (no change to the body)
  • Car car = _cars[i]; becomes Car * car = _cars[i];
  • car != *this becomes *car != *this
  • and all cases of car. become car->

The output is now:

- Collide ----------------------------
Car_01: x=-14, y=32, width=22, height=16, xMax=8, yMax=48, bitmap=16 
Car_02: x=7, y=32, width=22, height=16, xMax=29, yMax=48, bitmap=16 

I also changed some other things as I was trying to work out what was going on, so to make sure all the listed changes work, I got a fresh copy and made the aforementioned changes.
I discovered it did not work, so I had to spend a bit more time trying to find out which of my other changes made it work.

Luckily it was only one extra change:

  • Serial.println("- Collide ----------------------------"); to Serial.println(F("- Collide ----------------------------"));

Which basically implies the reason it was freezing up was due to either lack of memory or stack overflow. (And technically stack overflow is actually just a special case of running out of memory.)

I won’t call it a victory until you confirm that this works, but I’m pretty confident that those changes will fix the problem.


The real root cause of the problem though is that (with respect) it seems you don’t yet fully understand what the differences between pointers, references and values are.

The short version is (with a bit of handwaving because the truth is actually much scarier):

  • Values are the object itself - the actual data of the object, in memory. (When you pass an argument to a function by value, you are actually copying the entire object and pushing that copy on the stack.)

  • Pointers are actually just memory addresses. The number be any memory address, not just valid ones. (Passing a pointer to a function is just passing a number as wide as the address size of the CPU.)

  • References are like pointers that cannot be null and don’t have to be dereferenced with * or ->. You use them like values, but they can change the data that they reference like a pointer. (Also they must always be initialised either at the point of declaration or in a constructor.)
    (Passing a reference to a function effectively gets turned into passing a pointer that’s guarateed not to be null.)

If you’d like a more in depth explanation or have any questions, feel free to ask (either here or via PM, whichever you think is more suitable).

That explains it. This is what totally threw me … that it appeared some of the properties were set and others not.

This is the C# / Java in me where an object reference is just that and they can be added to an array with no issues as they are always references.

I understand the concepts but not being a C++ developer, I can easily get myself in a knot!

Thanks for your input, the game is moving along nicely now (to the left of course). Its still got a long, long way to go before it will be complete.

1 Like

Yeah, realising that the array copying the values would explain the results that were getting spat out was a moment of real epiphany.

Out of interest, you could have kept the array as an array of Car and just accessed the cars from the array instead of using the standalone ones (which would actually save space), but I opted for the path of least change.

Yeah, C++ is a totally different dragon.
It’s worth it though, to be able to choose where the data goes. Being able to choose to put an object on the stack really helps with performance (both speed and memory usage).
C# does have a similar feature in what it calls structs which are value types rather than reference types.

The main reason I suggested it is because I could see you made some similar mistakes in other places (e.g. you’re copying the Arduboy2 object instead of passing pointers to it or references of it. Not all of Arduboy2's state is static so that’s going to mess up some of the features.)

That’s fair enough. It takes a lot of practice because there are lots of subtle differences.