Link to home
Start Free TrialLog in
Avatar of atomicgs12
atomicgs12Flag for United States of America

asked on

How to handle pointers to pointers C++

I was wondering what is the correct to handle pointers to pointers, that is passing them into a function and delete them on return if the function receiving the pointers creates the memory but does not release it.

Say I have some code as below:
char *pInBuff;

ReturnData(char** pData);

Where ReturnData is defined something like:
ReturnData(&pInBuff)
{
     *pInbuff = new char[128];

     // load pInBuff with data
}

What is proper way to free pInBuff? Is it a simple delete pInbuff, delete [] pInBuff or something else?

Thanks
ASKER CERTIFIED SOLUTION
Avatar of Todd Gerbert
Todd Gerbert
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
If you have a function that allocates the memory, I would create a corresponding function in the same compilation unit that de-allocates the memory.

Something like below.


Or better yet : since you're using C++, you could :

(a) make use of SQL data structures like std::string

(b) encapsulate the buffer in a class that has a destructor that takes care of cleaning up the memory automatically

Either of the latter two are preferred over the example below.
void getData(char** pInBuff) {
    *pInbuff = new char[128];

    // load pInBuff with data
}

void cleanData(char** pInBuff) {
    delete [] *pInBuff;
    *pInBuff = 0;
}

// and then :

char* inBuff = 0;
getData(&inBuff);
// use inBuff ...
cleanData(&inBuff);

Open in new window

To make it a little clearer, wrap the whole thing in a class and then pass an instance of the class around


// file MyBuffer.h
class MyBuffer
{
private:
   char* m_buffer = NULL;

protected:
   void Empty();
   
public:
   MyBuffer();
   ~MyBuffer();
   
   void CleanData();
 }

 // file MyBuffer.cpp
MyBuffer::~MyBuffer
{
    Empty();
}

MyBuffer::Empty()
{
    if ( m_buffer != NULL )
    {
        delete [] m_buffer;
        m_buffer = NULL;
    }
}

MyBuffer::CleanData()
{
     Empty();
}

bool MyBuffer::IsEmpty()
{
    return ( m_buffer == NULL )
}

bool MyBuffer::LoadData( const char* data )
{
    bool retStatus = false;
    
    if ( IsEmpty() )
    {
        if ( data != NULL ) && ( strlen( data ) > 0 )
        {
            m_buffer = new char[strlen( data ) + 1];
            retStatus = true;
        }   
    }
    
    return retStatus;
}

// in file using this class

// on stack
{
    MyBuffer buff;
    char* TestData = "This is test data";

    buff.LoadData( TestData);
}

// or on heap
{
    MyBuffer buff = new MyBuffer();
    char* TestData = "This is test data";

    buff->LoadData( TestData);
    delete buff;
}

Open in new window

opps a little too quick
last bit should read

// in file using this class

// on stack
{
    bool AllOK = true;
    MyBuffer buff;
    char* TestData = "This is test data";

    ALLOK = buff.LoadData( TestData);
    if ( ALLOK )
    {
        // success
    }
    else
    {
        // error
    }
}

// or on heap
{
    bool AllOK = true;
    MyBuffer buff = new MyBuffer();
    char* TestData = "This is test data";

    AllOK = buff->LoadData( TestData);
    if ( ALLOK )
    {
        // success
    }
    else
    {
        // error
    }

    delete buff;
}

Open in new window

why not simply use std::vector?

std::vector<char> v;
getdata(v);
...
void getdata(std::vector<char> & v)
{
    v.resize(128, ' '); // 128 space characters
}

Open in new window


you don't need care for allocation or deallocation yourself. and the called function freely can increase the buffer as needed. and if you need a char* pointer you get it by &v[0] (only if v.size() > 0).

Sara
Wow - just noticed a typo in my previous post :

>> (a) make use of SQL data structures like std::string

STL of course, not SQL hehe.
Hmm, perhaps I read the question too literally. ;)
Avatar of atomicgs12

ASKER

tgerbert - Are you second quessing yourself? I appreciate all the cool solutions from everyone but I was just looking for something simple and straight forward.

delete [] pInBuff; should be fine, yes?
Then in that case, yup, delete [] pInBuff. Though I do like Infinity's comment.
as per Infinity's solution:
a. I need this to be Unicode so std:string is out
b. The user may what to keep the data around even after the object that allocated it is destroyed

Thanks to all
a: std::wstring

b: the caller can control the lifetime of the data by controlling the scope of the object
>> a. I need this to be Unicode so std:string is out

Your current approach of using a char* would be out too ;)
This remark also doesn't invalidate the point I was trying to make ... which was that you have several options that are better than your current approach. One of them is to use an existing solution (whether that be std::string or something else).


>> b. The user may what to keep the data around even after the object that allocated it is destroyed

As tgerbert said : if you want to keep the object, then keep it - nothing is forcing you to clean up the object at any other time than the time that you choose.
The point of encapsulating the buffer in a class, is to make your life easier, by hiding the memory allocation details from the calling code.