C++ and memory management

So I’ve come to OF from a post-Java programming background, with garbage collection and lots of features where I basically don’t have to care (too much) about memory management. The project I’m working on could be left running for days, so I just want to check a general scenario to see if I would have to explicitly delete something in order to avoid a memory leak. Any guidance much appreciated.

Say I have a struct that has a property vector<glm::vec3> positions, and I want to update it with totally new values. If rather than

s.positions.resize(0);
for (int i = 0; i < someNumber; i++) {
// add new values
}

I instead do

s.positions = newVectorOfPositions;

Will the old value be hanging around in memory somewhere, or will that get picked up and destroyed? If it were a primitive type like a float I wouldn’t be concerned, but presumably memory has been allocated for the old set of values, so should it be a shared_ptr or unique_ptr or similar?

Thanks in advance!

Is totally fine.
the vector object takes care of memory allocation, as long as the class of the values is copyable (if it is not the compiler would complain).

As a rule of thumb you only need to worry about memory management if you use the new keyword.
Notice that sometimes new is used to initialize shared_ptr or unique_ptr. In that case you can ignore the previous rule, yet it is a more recommended practice to use instead make_shared or make_unique instead, which makes it easier to spot if there is somewhere to deal with memory management.

so for if you have either:

unique_ptr<SomeClass> u = unique_ptr<SomeClass>(new SomeClass(...));
shared_ptr<SomeClass> s = shared_ptr<SomeClass>(new SomeClass(...));

use instead

unique_ptr<SomeClass> u = make_unique<SomeClass>(..);
shared_ptr<SomeClass> s = make_shared<SomeClass>(...);

There is a caveat still, be aware of circular referencing when using shared_ptr which means that you have 2 shared_ptr where each store a shared_pointer to the other, as the following.

class Something{
public:
shared_ptr<Something> other = nullptr;
};
shared_ptr<Something> A = make_shared<Something>();
shared_ptr<Something> B = make_shared<Something>();
A->other = B;
B->other = A;

It will work but you’ll be leaking memory.
Instead it should be:


class Something{
public:

void setOther(shared_ptr<Something> _other)
{
other = _other;
}

shared_ptr<Something> getOther(){
if(!other.expired()){
return other.lock();
}
return nullptr;
}
private:
weak_ptr<Something> other = nullptr;
};
shared_ptr<Something> A = make_shared<Something>();
shared_ptr<Something> B = make_shared<Something>();
A->setOther(B);
B->setOther(A);

So, if you follow this practices, the only reason to worry about memory management would be if you use the new keyword

2 Likes

Brilliant, thanks for that- looks like I’m (probably) fine for now then. Since you bring up pointers again- I’m mostly only using pointers in function calls when I don’t want the copy constructor to be used, i.e.

someFunction(typeA* a) {
  vbo.setAttributeBuffer(someLocation, *a->buffer, 3, 0, 0);
}
//...
someFunction(&myObjectThatHasABufferAndOtherStuff);

…or whatever. I’m guessing these are transient as well, or should I be using shared_ptr unique_ptr for these?

Hello,

In this case this is often better to do

someFunction(typeA & a) {
  vbo.setAttributeBuffer(someLocation, a.buffer, 3, 0, 0);
}
//...
someFunction(myObjectThatHasABufferAndOtherStuff);

because this help to avoid dandling pointer risks.

There is no need to use a smart pointer here.

Perhaps this one may be even better:

someFunction(ofBufferObject &buffer) {
  vbo.setAttributeBuffer(someLocation, buffer, 3, 0, 0);
}
//...
someFunction(myObjectThatHasABufferAndOtherStuff.buffer);

This second solution can improve class decoupling: the class that define someFunction could ignore details about typeA. But this is a general advice that may be relevant or not, depending on your actual case.

Some good advices about memory management in the C++ Core Guidelines.

1 Like

Wonderful, thanks guys.

As @lilive points, in the case of functions it is much better to use references. More oven, make those references const when ever you can.

someFunction(const ofBufferObject &buffer) {....}
1 Like