Search code examples
c++stdesp32stdmap

Structs in std::map<int,struct> memory leaking?


I have the following struct and map

struct dataStruct{
  unsigned long time;
  int32_t ch0;
  int32_t ch1;
  uint8_t state;
  int16_t temp;
  uint16_t vbat;
  int8_t rssi;
  };

std::map<uint32_t,struct dataStruct> uuidData = {};

And a loop which waits for new data and fills the map with it. (1)

for(;;)
{
    if (data_debug.is_new_data_available())
    {
        uint32_t uuid = data_debug.get_ID();
        uuidData[uuid] = {
            millis(), 
            data_debug.get_data1(), 
            data_debug.get_data2(), 
            data_debug.get_state(), 
            data_debug.get_temperature(),
            data_debug.get_battery_level(),
            data_debug.get_RSSI()
        };
    }
}

This works, but when I get data with an existing UUID, my intuition is that the old struct never gets deleted and will eventually leak.

I had an alternative code (2) below that tried to bypass this, but when the UUID was duplicated, the struct was filled with garbage. Is the code in (1) leaking or c++ automatically frees the space when the struct is no longer needed? (I know this sounds like garbage collection, but i have no idea how c++ handle structs)

(2)

    if(uuidData.count(uuid)){
        uuidData[uuid].time = millis();
        uuidData[uuid].ch0 = data_debug.get_data1();
        uuidData[uuid].ch1 = data_debug.get_data2();
        uuidData[uuid].state = data_debug.get_state();
        uuidData[uuid].temp = data_debug.get_temperature();
        uuidData[uuid].vbat = data_debug.get_battery_level();
        uuidData[uuid].time = data_debug.get_RSSI();
    }
    else{
        uuidData[uuid] = {
            millis(), 
            data_debug.get_data1(), 
            data_debug.get_data2(), 
            data_debug.get_state(), 
            data_debug.get_temperature(),
            data_debug.get_battery_level(),
            data_debug.get_RSSI()
            };
}

Solution

  • Let's break down what happens with example 1 (the correct way to do this task )

    uuidData[uuid] = {
                millis(), 
                data_debug.get_data1(), 
                data_debug.get_data2(), 
                data_debug.get_state(), 
                data_debug.get_temperature(),
                data_debug.get_battery_level(),
                data_debug.get_RSSI()
            };
    

    The

            {
                millis(), 
                data_debug.get_data1(), 
                data_debug.get_data2(), 
                data_debug.get_state(), 
                data_debug.get_temperature(),
                data_debug.get_battery_level(),
                data_debug.get_RSSI()
            };
    

    creates and initializes a temporary dataStruct variable, an automatic variable that will only exist until it's no longer needed. When the expression ends, the temporary goes out of scope and is destroyed.

    uuidData[uuid]
    

    looks in uuidData for a key that matches uuid. If it does not find one, it creates an empty dataStruct and maps it to the key. This new dataStruct is managed by uuidData. Where it comes from and where it goes is not your concern. Once a dataStruct mapped to uuid exists, a reference to it is returned.

    Now we have a reference to an existing dataStruct in the map and the temporary dataStruct. The = simply copies the contents of the temporary on the right into the the object represented by the reference on the left. Whatever was in the object on the left is overwritten but the object is still there and still managed by uuidData. It will be properly destroyed and deallocated when it is either removed from uuidData or uuidData goes out of scope. The temporary is destroyed automatically (ergo the name Automatic variable) for you when the expression is complete.

    There is no possibility for a leak here.