Search code examples
c++raii

c++ new C array allocation, RAII or simple shared_ptr / boost::shared_array


I am learning c++ and i stumbled once again on a new issue.

I do need to allocate a C array for a library to use, but in a safe way, ofcourse. I already found that delete[]; at the end of method fails miserably.

OLD, not that good:

float *buf;

try {
    buf = new float[daswidth*chans_info.chans*sizeof(float)];
}
catch (std::bad_alloc& ba) // sometimes it throws ! so i do need to stop my execution.
{
    if (DEBUG) tekstasFormat(L"WARNING: bad alloc caught: %s", ba.what());
    return; // skip this iteration then.
}

//... OUR CODE

delete[] buf;

So what i tryed to use which works perfectly instead of my old allocation and deletion:

float *buf;

std::shared_ptr<float> safe_buf(new float[daswidth*chans_info.chans*sizeof(float)], [](float *p) { delete[] p; });

// OR BOOST EQUIVALENT
boost::shared_array<float> safe_buf(new float[daswidth*chans_info.chans*sizeof(float)]);

buf = safe_buf.get();

And we do never leak, everyone is happy. But how to catch new_alloc throws now ?!

if i will allocate shared_ptr in a new{} scope after new it gets destroyed... Explain me and noobs like me for the future a bit more. How to handle exceptions in this case ?


Solution

  • Prefer std::unique_ptr to shared_ptr. It's far faster.

    std::unique_ptr<float[]> buf; //construct
    
    try {
        buf.reset(new float[daswidth*chans_info.chans*sizeof(float)]); //give memory
    }
    catch (std::bad_alloc& ba) // sometimes it throws ! so i do need to stop my execution.
    {
        if (DEBUG) tekstasFormat(L"WARNING: bad alloc caught: %s", ba.what());
        return; // skip this iteration then.
    }
    

    However, as Ben said, there's pretty much no reason to use a float[] like this. Instead, use std::vector. Yes, even for most* C interop.

    std::vector<float> buf;
    
    try {
        buf.resize(daswidth*chans_info.chans*sizeof(float)); //give memory
    }
    catch (std::bad_alloc& ba) // sometimes it throws ! so i do need to stop my execution.
    {
        if (DEBUG) tekstasFormat(L"WARNING: bad alloc caught: %s", ba.what());
        return; // skip this iteration then.
    }
    
    function_expecting_float_pointer(buf.data()); //use .data() to get the `float*`
    

    *don't use std::vector if the C code will ever "reallocate" the pointer for you. Instead, use std::unique_ptr, and (1) release the memory, (2) pass to C, (3) reset with pointer returned from C.