views:

276

answers:

4

Hello, I have started out to write a simple console Yahtzee game for practice. I just have a question regarding whether or not this function will leak memory. The roll function is called every time the dices need to be re-rolled.

What it does is to create a dynamic array. First time it is used it will store 5 random values. For the next run it will only re-roll all except for the dice you want to keep. I have another function for that, but since it isn't relevant for this question I left it out

Main function

int *kast = NULL;           //rolled dice
int *keep_dice = NULL;    //which dice to re-roll or keep

kast = roll(kast, keep_dice);
delete[] kast;

and here's the function

int *roll(int *dice, int *keep) {

    srand((unsigned)time(0));
    int *arr = new int[DICE];
    if(!dice)
    {
        for(int i=0;i<DICE;i++)
        {

            arr[i] = (rand()%6)+1;
            cout << arr[i] << " ";
        }
    }
    else
    {
        for(int i=0;i<DICE;i++)
        {
            if(!keep[i])
            {
                dice[i] = (rand()%6)+1;
                cout << "Change ";
            }
            else
            {
                keep[i] = 0;
                cout << "Keep ";
            }
        }
        cout << endl;
        delete[] arr;
        arr = NULL;
        arr = dice;

    }
    return arr;
}
+4  A: 

Use a (stack-allocated) std::vector instead of the array, and pass a reference to it to the function. That way, you'll be sure it doesn't leak.

Thomas
I have consider to use vector, but since I already know how large list I want I thought using array would be better.
starcorn
@klw: What? That doesn't make sense. A `std::vector` is the standard and safer version of your code, knowing the size doesn't make sense, just call `resize` or `reserve` on the vector.
GMan
Indeed; if you use the proper constructor, so that no reallocations are needed, you won't pay for those. It is probably as fast as an array, and a heck of a lot easier.
Thomas
@klw: FYI, recent versions of gcc and the next C++ standard will have std::array<CONST_SIZE> arr;which would do what you want. The others commenters are, strictly speaking, wrong; vector<type> allocates its array dynamically, and this is sometimes unwanted, and even sometimes impossible. I'm not saying that that is true in your case, though; if your developing an app on a full OS, vector<> is probably fine.
Tim Schaeffer
@Tim As far as I can see, everyone is recommending vector over a *dynamic* array, hence there's already allocation going on.
James Hopkin
@Tim: Which commentators said vector isn't dynamically allocated? Also, that's technically not always true; one could easily write an allocator that uses stack-space.
GMan
+13  A: 

Yes, it can leak. Just for example, using cout can throw an exception, and if it does, your delete will never be called.

Instead of allocating a dynamic array yourself, you might want to consider returning an std::vector. Better still, turn your function into a proper algorithm, that takes an iterator (in this case, a back_insert_iterator) and writes its output there.

Edit: Looking at it more carefully, I feel obliged to point out that I really dislike the basic structure of this code completely. You have one function that's really doing two different kinds of things. You also have a pair of arrays that you're depending on addressing in parallel. I'd restructure it into two separate functions, a roll and a re_roll. I'd restructure the data as an array of structs:

struct die_roll { 
    int value;
    bool keep;

    die_roll() : value(0), keep(true) {}
};

To do an initial roll, you pass a vector (or array, if you truly insist) of these to the roll function, which fills in initial values. To do a re-roll, you pass the vector to re-roll which re-rolls to get a new value for any die_roll whose keep member has been set to false.

Jerry Coffin
Alternatively, the use of `auto_ptr` is beneficial.
Travis Gockel
Sry didn't mention that those cout is only used to bug check. Anyway, other than that is the function leak-proof and the arrays deleted correct?
starcorn
@klw:Yes, as long as you assure that no exception can be thrown between allocating and freeing the memory, your code should be free of memory leaks. That condition, however, means your code is (no pun intended) exceptionally fragile at best.
Jerry Coffin
@Travis: you better not use `auto_ptr` with memory allocated with `new []`.
David Rodríguez - dribeas
@klw: At Travis indicates the function is not 'exception tolerant'. This is bad design in C++ and means that anybody that comes after you to modify the code may be caught off guard (as they would expect the author or previous maintainer to be competent and not write code that is dangerous in the presence of exceptions). Also providing an interface that exposes RAW pointers is ambiguous on its intent. There is no indication if you are passing ownership or retaining ownership based on the function declaration (In C++ this is bad as we have a major issue with ownership semantics)
Martin York
+4  A: 

The way you allocate memory is confusing: memory allocated inside the function must be freed by code outside the function.

Why not rewrite it something like this:

int *kast = new int[DICE];           //rolled dice
bool *keep_dice = new bool[DICE];    //which dice to re-roll or keep
for (int i = 0; i < DICE; ++i)
    keep_dice[i] = false;

roll(kast, keep_dice);

delete[] kast;
delete[] keep_dice;

This matches your news and deletes up nicely. As to the function: because we set keep_dice all to false, neither argument is ever NULL, and it always modifies dice instead of returning a new array, it simplifies to:

void roll(int *dice, int *keep) {
    for(int i=0;i<DICE;i++)
    {
        if(keep[i])
        {
            keep[i] = false;
            cout << "Keep ";
        }
        else
        {
            dice[i] = (rand()%6)+1;
            cout << "Change ";
        }
    }
    cout << endl;
}

Also, you should move the srand call to the start of your program. Re-seeding is extremely bad for randomness.

Thomas
thanks for the input
starcorn
A: 

My suggestion would be to take time out to buy/borrow and read Scott Meyers Effective C++ 3rd Edition. You will save yourselves months of pain in ramping up to become a productive C++ programmer. And I speak from personal, bitter experience.

Steve Townsend
thanks for the suggestion
starcorn