I am new to Object Oriented Programming and trying to learn the ins and outs to write better quality code in the future.
There is one issue that has been driving me crazy for the last days. I am trying to pass a variable down from the main program over an object to another object one layer deeper. I broke the problem down to 5 short files:
Search_for_error
#include "MainClass.h"
#define VAR 7
MainClass object(VAR);
void setup() {
Serial.begin(115200);
object.init();
}
void loop() {
// put your main code here, to run repeatedly:
}
00:17:34.203 -> var in object: 7
00:17:34.203 -> var in subobject: 7
The output that I actually get is
00:55:56.420 -> var in object: 7
00:55:56.420 -> var in subobject: 0
00:55:56.420 -> var in object: 7
00:55:56.420 -> var in subobject: 0
So:
Why the heck is var in subobject not 7? In MainClass.cpp, the line this->var = var; is right before the definition of subobject and the serial output comes after that and shows that var is 7.
Why are there 4 lines of serial output when I think I only should create 2 (one output line in MainClass and one in SubClass)?
What am I supposed to write so that the code does what I expect it to do?
when you create object of type MainClass is has to create all your private variables and one of those is subobject but inside MainClass::MainClass(), you are creating another variable subobject not assigning to it.
AFAIK C++ constructors work bottom up. Subclasses are initialized before the more derived classes. That's why you'll often find begin() methods that initialize previously constructed objects in some controllable way.
You mean that the header file MainClass.h and the code file MainClass.cpp create two separate entities of subobject that both have the same name, so they are both called by object.init() ? If that is the case, I had no idea that there are languages allowing for several objects with the same name in the same namespace.
i know there are plenty of cases where classes have a begin(). but doesn't the constructor serve that purpose and allow parameters for customized construction?
Ideally, yes.
However, for Arduino specifically, you have to be careful with what you do in a constructor, because the constructors of global variables are executed before main/setup runs, so the hardware you want to access might not be initialized yet. That's why many libraries have a begin() function to call after initializing the hardware.
In standard “desktop” C++ programming or embedded systems where a separate bootloader initializes the hardware, this is usually not a concern.
ok. but i think this is an artifact of having global objects (e.g. Serial) to make Arduino easier to use and would normally be poor programming practice.
True, but because of the separate setup() and loop() functions, Arduino forces people to use globals, and beginning programmers will use globals anyway.
So it's best to ensure that your classes don't break when instantiated as globals. Especially since debugging an Arduino that crashes before even reaching main() is not a pleasant experience.
If your initialization doesn't access any hardware, this is not an issue of course, and you should just use the constructor (but that means you can't Serial.print() anything either).
That is not 'constructing' the contained object but creating a new local object. The contained-object constructors get called between: MainClass::MainClass(byte var)
and {
To pass arguments to the constructor of the contained object, use this syntax:
Okay, thank you everybody for the extensive feedback! I learned that I do not yet understand some basic principles of C++ programming that I did not know I needed (like a main() method in Arduino that seems to be buried somewhere, or the : operator after a class definition. I will go through all of your answers and try to understand them, but it will take some time. Thanks for giving me clarity about what I need to work on.
When you upload a sketch, the uploading app ("avrdude") connects to the Arduino serial port. That means the Serial Monitor app has to disconnect while the uploader is running.
When the uploader finishes uploading, your sketch starts to run. After running for about 150-180 milliseconds (thousands of instruction cycles) the Serial Monitor notices that the Arduino serial port is available and re-connects.
The Arduino UNO has an Auto-Reset feature to allow you to upload sketches without manually pressing the reset button. This works by resetting the Arduino when a serial connection is established. That gives the bootloader a chance to look for an upload.
When Serial Monitor re-connects, the Arduino is reset. This means that any actions, including output, performed in the first 150-180 mlliseconds of running are repeated.
That's why I put "Serial.begin(115200);" and "delay(200);" at the top of setup().
1. The Uploader and Serial Monitor are seperate Application Softwares which can be invoked from the Arduino IDE by clicking on the "Upload" Command and "Serial Monitor" Icon respectively. They (the Apps) communicate with the Arduino UNO over the same Communication Port (Virtual UART/Real USB) and also communicate between themselves based on the logic of the following (Fig-1) conceptual connectivity diagram.
2. Assume that a sketch is running in the UNO and the SM (Serial Monitor) is opened. The SSM swicth (bi-directional 3-state) is closed and SUL switch is opened. The SM is exchanging message with UNO. The Uploader Apps is OFF beecause of S-signal (coming from SM) and is also disconnected from UNO via SUL swicth.
3. Let us click on the Upload Command of the IDE. It is observed that SM is halted because of S/-signal (coming from UL) and there is no communication between SM and UNO.
4. At the end of uploading process, the SM becomes automatically active and resumes communication with the UNO.
So, after upload, the sketch starts running immediately and a serial output is generated. But because no serial connection is open yet, the output is stored in some kind of buffer. That Buffer is somehow not cleared when the board resets. A little moment later the serial connection is re-established, which resets the board. Then, because the sketch restarts, also a second serial output is generated, added to the buffer, then both sent together.
The part about resetting the board after reestablishing the connection makes total sense. Otherwise you might miss the first few messages after an upload because the serial connection was not established, which would completely throw off the Arduino target audience.
Another way to do this would be to have a buffer that stores all serial output when there is no serial connection, and then send it once the connection is established.
Now somehow both solutions seem to be implemented at the same time, leading to double output. I am not convinced that this is the best solution, but it makes sense as explanation. Thanks for elaborating!
Why do you call the colon ( : ) is an operator? It is not in the operators' list of C/C++ Language!
I have encounered this symbol ( : ) in many instances of C/C++ Programming of which three are given below. I don't know what does it actually do; but, to many (in the net) the symbol appears working close to a punctuation mark.
1. In ternary operator -- ?:
int max = (a > b) ? a : b;
2. In the Constructor Function of a class to initialize the member variabes.
Rectangle::Rectangle (int x, int y) : width(x), height(y) { }
3. After the access specifier of class declaration.