How to avoid dynamic allocation of memory C++

[edit] Outside of this get method (see below), i'd like to have a pointer double * result; and then call the get method, i.e.

    // Pull results out
    int story = 3;
double * data;
int len;
m_Scene->GetSectionStoryGrid_m(story, data, len);

with that said, I want to a get method that simply sets the result (*&data) by reference, and does not dynamically allocate memory.

The results I am looking for already exist in memory, but they are within C-structs and are not in one continuous block of memory. Fyi, &len is just the length of the array. I want one big array that holds all of the results.

Since the actual results that I am looking for are stored within the native C-struct pointer story_ptr->int_hv[i].ab.center.x;. How would I avoid dynamically allocating memory like I am doing above? I’d like to point the data* to the results, but I just don’t know how to do it. It’s probably something simple I am overlooking… The code is below.

Is this even possible? From what I've read, it is not, but as my username implies, I'm not a software developer. Thanks to all who have replied so far by the way!

Here is a snippet of code:

void GetSectionStoryGrid_m( int story_number, double *&data, int &len )
{

    std::stringstream LogMessage;

    if (!ValidateStoryNumber(story_number))
    { 
            data = NULL;
            len = -1;
    }
    else
    {
            // Check to see if we already retrieved this result
            if ( m_dStoryNum_To_GridMap_m.find(story_number) == m_dStoryNum_To_GridMap_m.end() )
            {
                    data   = new double[GetSectionNumInternalHazardVolumes()*3];
                    len = GetSectionNumInternalHazardVolumes()*3;

                    Story * story_ptr = m_StoriesInSection.at(story_number-1);
                    int counter = 0;  // counts the current int hv number we are on

                    for ( int i = 0; i < GetSectionNumInternalHazardVolumes() && story_ptr->int_hv != NULL; i++ )  
                    {  
                            data[0 + counter] = story_ptr->int_hv[i].ab.center.x;
                            data[1 + counter] = story_ptr->int_hv[i].ab.center.y;
                            data[2 + counter] = story_ptr->int_hv[i].ab.center.z;

                            m_dStoryNum_To_GridMap_m.insert( std::pair<int, double*>(story_number,data));

                            counter += 3;
                    }
            }
            else
            {
                    data = m_dStoryNum_To_GridMap_m.find(story_number)->second;
                    len = GetSectionNumInternalHazardVolumes()*3;
            }
    }
}

Upvotes: 0

Views: 3681

Answers (3)

Max Lybbert
Max Lybbert

Reputation: 20021

This answer to this question relies on the lifetime of the doubles you want pointers to. Consider:

// "pointless" because it takes no input and throws away all its work
void pointless_function()
{
    double foo = 3.14159;
    int j = 0;
    for (int i = 0; i < 10; ++i) {
        j += i;
    }
}

foo exists and has a value inside pointless_function, but ceases to exist as soon as the function exits. Even if you could get a pointer to it, that pointer would be useless outside of pointless_function. It would be a dangling pointer, and dereferencing it would trigger undefined behavior.

On the other hand, you are correct that if you have data in memory (and you can guarantee it will live long enough for whatever you want to do with it), it can be a great idea to get pointers to that data instead of paying the cost to copy it. However, the main way for data to outlive the function that creates it is to call new, new[], or malloc. You really can't get out of that.

Looking at the code you posted, I don't see how you can avoid new[]-ing up the doubles when you create story. But you can then get pointers to those doubles later without needing to call new or new[] again.


I should mention that pointers to data can be used to modify the original data. Often that can lead to hard-to-track-down bugs. So there are times that it's better to pay the price of copying the data (which you're then free to muck with however you want), or to get a pointer-to-const (in this case const double* or double const*, they are equivalent; a pointer-to-const will give you a compiler error if you try to change the data being pointed to). In fact, that's so often the case that the advice should be inverted: "there are a few times when you don't want to copy or get a pointer-to-const; in those cases you must be very careful."

Upvotes: 0

Speed8ump
Speed8ump

Reputation: 1317

Consider returning a custom accessor class instead of the "double *&data". Depending on your needs that class would look something like this:

class StoryGrid {
    public:
        StoryGrid(int story_index):m_storyIndex(story_index) {
            m_storyPtr =  m_StoriesInSection.at(story_index-1);
        }
        inline int length() { return GetSectionNumInternalHazardVolumes()*3; }

        double &operator[](int index) {
            int i = index / 3;
            int axis = index % 3;
            switch(axis){
                case 0: return m_storyPtr->int_hv[i].ab.center.x;
                case 1: return m_storyPtr->int_hv[i].ab.center.y;
                case 2: return m_storyPtr->int_hv[i].ab.center.z;
            }
        } 
};

Sorry for any syntax problems, but you get the idea. Return a reference to this and record this in your map. If done correctly the map with then manage all of the dynamic allocation required.

Upvotes: 4

ericbn
ericbn

Reputation: 10948

So you want the allocated array to go "down" in the call stack. You can only achieve this allocating it in the heap, using dynamic allocation. Or creating a static variable, since static variables' lifecycle are not controlled by the call stack.

void GetSectionStoryGrid_m( int story_number, double *&data, int &len )
{
    static g_data[DATA_SIZE];
    data = g_data;
    // continues ...

If you want to "avoid any allocation", the solution by @Speed8ump is your first choice! But then you will not have your double * result; anymore. You will be turning your "offline" solution (calculates the whole array first, then use the array elsewhere) to an "online" solution (calculates values as they are needed). This is a good refactoring to avoid memory allocation.

Upvotes: 2

Related Questions