I have been stumbling over this problem for a while now. I am trying to return a pointer to an object to I can say
MyObject* obj = Manager::Create(int i, int j);
How do I correctly allocate memory so there are no leaks? I thought I was supposed to call new to make the memory on the heap but I have recently been told otherwise.
“I have been stumbling over this problem for a while now. I am trying to return a pointer to an object to I can say
MyObject* obj = Manager::Create(int i, int j);How do I correctly allocate memory so there are no leaks? I thought I was supposed to call new to make the memory on the heap but I have recently been told otherwise.”
Since you’re asking about how to allocate memory, and since that happens inside Manager::Create, the only reasonable interpretation I can see is that you’re the one writing the Manager::Create function.
So, first of all, do you really need a factory, and what, if anything, is the “manager” actually managing?
It is my impression that people coming from a Java background have a strong tendendcy to add needless dynamic allocation and factories and “managers” and singletons and envelope patterns and whatnot, that are generally ungood in C++.
Don’t.
For example, if your obj is only needed in a local scope, use automatic storage (stack based allocation and deallocation), which can be orders of magnitude more efficient than Java-like dynamic allocation:
MyObject obj( i, j );
It this is applicable, then the question “How do I correctly allocate memory so there are no leaks” has a very simple answer in C++: just declare the variable, as above.
This applies even if you have to return such an object from a function. Then just return by value (apparently copying the object). For example as with the foo::reduce function below,
#include <iostream> // std::cout, std::endl
#include <string> // std::string, std::to_string
namespace foo {
using namespace std;
class MyObject
{
private:
string description_;
public:
string description() const { return description_; }
MyObject( int const x, int const y )
: description_( "(" + to_string( x + 0LL ) + ", " + to_string( y + 0LL ) + ")" )
{}
};
ostream& operator<<( ostream& stream, MyObject const& o )
{
return stream << o.description();
}
int gcd( int a, int b )
{
return (b == 0? a : gcd( b, a % b ));
}
MyObject reduce( int const a, int const b )
{
int const gcd_ab = gcd( a, b );
return MyObject( a/gcd_ab, b/gcd_ab );
}
} // namespace foo
int main()
{
using namespace foo;
int const a = 42;
int const b = 36;
cout << MyObject( a, b ) << " -> " << reduce( a, b ) << endl;
}
Now let’s see how this concise, simple and efficient code can be made verbose, complex and inefficient by introducing needless dynamic allocation. I write “needless” because most of the traditional reasons for dynamic allocation have been obviated by the containers etc. of the standard C++ library and the facilities of the C++ language. For example, where previously you might have used pointers to avoid costly copying, and consequently getting into the question “i need to clean up, but was the object allocated on automatic storage or dynamically?“, with C++03 you could use smart pointers such as boost::shared_ptr to automate proper destruction regardless of the object’s origins, and with C++11 you can use move semantics to largely avoid the copying inefficiencies, so that the problem does not pop up in the first place.
So, the use of dynamic allocation in the code below is, with modern C++, very artifical and construed; it does not have any practical advantage.
But with this dynamic allocation, even if for this example it is artificial and construed, proper deallocation must be guaranteed, and the general way to do that is to use a smart pointer such as std::unique_ptr:
#include <iostream> // std::cout, std::endl
#include <memory> // std::unique_ptr, std::default_delete
#include <string> // std::string, std::to_string
namespace foo {
using namespace std;
class MyObject
{
friend struct default_delete<MyObject>;
private:
string description_;
protected:
virtual ~MyObject() {} // Restrics class to dynamic allocation only.
public:
typedef unique_ptr<MyObject> Ptr;
string description() const { return description_; }
MyObject( int const x, int const y )
: description_( "(" + to_string( x + 0LL ) + ", " + to_string( y + 0LL ) + ")" )
{}
};
ostream& operator<<( ostream& stream, MyObject const& obj )
{
return stream << obj.description();
}
int gcd( int a, int b )
{
return (b == 0? a : gcd( b, a % b ));
}
MyObject::Ptr reduce( int const a, int const b )
{
int const gcd_ab = gcd( a, b );
return MyObject::Ptr( new MyObject( a/gcd_ab, b/gcd_ab ) );
}
} // namespace foo
int main()
{
using namespace foo;
int const a = 42;
int const b = 36;
MyObject::Ptr const pData( new MyObject( a, b ) );
MyObject::Ptr const pResult( reduce( a, b ) );
cout << *pData << " -> " << *pResult << endl;
}
Note the protected destructor and the friend declaration of the code that calls the destructor. The protected destructor ensures that no static or automatic instances can be created, that only dynamic allocation can be used (e.g., one might impose this restriction just to make it easier to implement the class and to ensure to no automatic object is linked to in a dynamic data structure). The friend declaration makes the protected destructor accessible to the standard library’s object destruction function – which, however, is unfortunately only used by unique_ptr.
I’m mentioning and exemplifying this because you have a factory function, which is sometimes used to restrict a class to dynamic allocation (especially by folks coming from Java). A factory function is ungood for this purpose, because with n constructors you need n factory functions. In contrast, with the protected destructor you only need a common deleter function, and as shown above, that’s provided by the standard library.
So, upshot, generally the answer to the question “How do I correctly allocate memory so there are no leaks” is to delegate deallocation responsibility to the language or the standard library, or to other library components. First and foremost this means using automatic storage (local variables) and value returns. But when there is a need, it also includes using standard library collection classes such as std::vector and std::map. Only if those do not provide the desired functionality, consider dynamic allocation. And then delegate the deallocation responsibility to smart pointers such as std::unique_ptr and std::shared_ptr.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With