Search code examples
arduino-c++arduino-esp32

c++ esp32 - pointer to object


I have a project for ESP32 (Vscode/platformIO) with this code working: BCls receives in its constructor a pointer to an instance of ACls and assign it to its private _a. So in main.cpp, I just create and instance of ACls, then inject a pointer to it into the constructor of BCls:

main.cpp

#include "ACls.h"
ACls aa;

#include "BCls.h"
BCls bb(aa);

void setup() {
    bb.doSomething("hello");
}

BCls.h

#include "ACls.h"

class BCls {
    public: 
        ACls *_a;

        //ctors
        BCls();
        BCls(ACls *aa);
}

BCls.cpp

#include "BCls.h"
#include "ACls.h"

BCls::BCls() {
}

BCls::BCls(ACls *aa) {
    _a = aa;
}

The problem:

But now I need to modify it so that, if BCls does not receive in its constructor an ACls instance, it must instantiate it internally and assign it to _a. I tried this but it does not work (compiles ok but fails in runtime):

main.cpp

#include "BCls.h"
BCls bb;

void setup() {
    bb.doSomething("hello");
}

BCls.h

class BCls {
    public: 
        ACls *_a;

        //ctors
        BCls();
        BCls(ACls *aa);
}

BCls.cpp

#include "ACls.h"

BCls::BCls() {
    //how to instantiate here an object of class ACls and assign a pointer to it to BCls._a??
    //I tried this but its not working:
    ACls aTmp;
    _a = &aTmp;
}

BCls::BCls(ACls *aa) {
    _a = aa;
}

the exception is

rst:0x8 (TG1WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1284
load:0x40078000,len:12808
load:0x40080400,len:3032
entry 0x400805e4

So, how should this be done?

MORE INFO:

I will try to explain deeper why I want to do this: Lets say I have 3 classes:

  • SdCls -> manages everything related to a SD card module.
  • GpsCls -> manages everything related to a gps module.
  • SensorCls -> manages everything related to a temperature sensor.

And lets say both GpsCls and SensorCls, need to log some data on the sd card. and also the project's main file needs to use the sd card to read files or whatever.

So right now, in projects main file, I instantiate SdCls, then instantiate GpsCls and SensorCls, passing the SdCls instance to their constructors. so now I can use the SD card in the projects main file, and both the gpsCls and SensorCls can use that same instance of SdCls to do their things. And everyone is happy.

But now I want these classes to be reusable in other projects. and lets say in another project, the only one that needs to access the SD card is the GpsCls. My idea with this was that, instead of instantiating the SdCls in the project main file, that I could just create the GpsCls object calling its empty constructor, and then let the GpsCls class instantiate the SdCls object it needs (as it will only be used by GpsCls)

I hope this is clearer now...


Solution

  • The problem you are facing is a result of the lifetime of the ACls object you want to keep a pointer to in BCls, by declaring it in the constructor, when the constructor returns, aTmp goes out of scope and is destroyed and the pointer saved no longer points to a valid ACls object.

    There are a few options to solve this:

    1. If you can afford creating a ACls for every BCls object, simply create a member ACls:
    class BCls {
      ACls _m_a;
      ACls *_a;
    public:
      BCls() : _a(&_m_a) { }
      BCls(ACls *aa) : _a(aa) { }
    };
    
    1. If you only need to create one BCls or they can share ACls object, you can declare the aTmp as static:
    class BCls {
      ACls *_a;
    public:
      BCls() {
        static ACls aTmp; // Note this will be shared for all BCls objects that use this constructor 
        _a = &aTmp;
      }
      BCls(ACls *aa) : _a(aa) { }
    };
    
    1. If you cannot do either of the above, the best bet is to dynamically allocate ACls (or you could allocate from a static pool but that involves more work that might not be worth it on something like an ESP32; also remember to delete the object in the destructor for the BCls object if needed to not leak memory):
    class BCls {
      ACls *_a;
    public:
      BCls() {
        _a = new ACls();
      }
      BCls(ACls *aa) : _a(aa) { }
    };