Search code examples
c++pointersnew-operator

Assigning a value to a null ptr-to-ptr in C++


I am trying to write a function that receives a pointer and makes it point to a new object. In order to do this, I am using a ptr-to-ptr. This is what my function looks like:

void modifyPtr(int ** ptrToPtr)
{
    *ptrToPtr = new int(42);
    return;
}

I would like my function to work even when ptrToPtr is a nullptr:

int ** ptrToPtr = nullptr;
modifyPtr(ptrToPtr);

My first implementation of the function is unsafe for this, because it would dereference a nullptr. So I thought of the following:

void modifyPtr(int ** ptrToPtr)
{
    ptrToPtr = &(new int(42));
    return;
}

But taking the address of the pointer returned by the new operator is not allowed, producing the following error during compilation:

error: lvalue required as unary '&' operand

So I thought of one last option:

void modifyPtr(int ** ptrToPtr)
{
    int * temp = new int(42);
    ptrToPtr = &temp;
    return;
}

But this produces a segmentation fault during runtime, which I expected because as soon as the temp pointer goes out of scope, it is deleted, resulting in a memory leak.

Is there a way to make my function work with a nullptr?


Solution

  • To make a function that modifies a pointed object work with a null pointer, I assume that you want to create a new object. This is a bad idea: When the input is non-null, it does not create a new pointer object. Doing something completely different with different inputs is a bad design and will easily confuse the reader of the code.

    Instead, I suggest that you don't attempt to make the function "work" with null pointer input but instead require that an address of a pointer is passed, rather than null). This can be enforced at compile time by using a reference argument instead of a pointer to pointer to prevent the caller from making a mistake:

    void modifyPtr(int*& ptr)
    {
        ptr = new int(42);
    }
    // Usage
    int* ptr;
    modifyPtr(ptr);     // OK
    modifyPtr(nullptr); // does not compile; as was intended
    

    Or, maybe you intend to always create a new pointer as you do in your broken example, in which case the argument is completely pointless. You could simply return a pointer:

    int* createObject()
    {
        return new int(42);
    }
    // Usage
    int* ptr = createObject();
    

    Ignoring the improved design that I suggested, you can make the function create a new pointer and return its address. It's simply not possible using automatic storage. You could use dynamic storage instead. But even then, you would have to pass the pointer to pointer by reference in order to actually modify it:

    void modifyPtr(int**& ptrToPtr)
    {
        if(!ptrToPtr)
            ptrToPtr = new int*;
        *ptrToPtr = new int(42);
    }
    

    However, you must remember that being dynamically allocated, both the pointer to pointer, and the pointer to object must be deleted manually. This design has no advantages to what I suggested earlier.


    But this produces a segmentation fault during runtime, which I expected because as soon as the temp pointer goes out of scope, it is deleted, resulting in a memory leak.

    The memory is indeed leaked, but a memory leak does not cause a segmentation fault. Your suggested function only modifies local copy of the pointer to pointer, so whatever variable was passed to the function would remain unmodified. If that value is indeed null, then subsequent dereferencing of the pointer would have undefined behaviour. Resulting in a segmentation fault if you're lucky.


    Finally, allocating dynamic memory outside of a container is a bad idea in general. You should use a smart pointer instead. The final version that I actually recommend would be:

    std::unique_ptr<int> createObject()
    {
        return new int(42);
    }
    

    Of course, this particular example is so trivial that writing a function for it makes no sense in the first place.