Textures need to be destroyed with the SDL_DestroyTexture
function, and you're not doing that, which is leading to the memory leak.
Incidentally, manually calling the destruction function on a raw pointer when you no longer need it is error prone (you can easily forget a case where it needs to go) and unnecessary, with the wonderful world of Resource Acquisition Is Initialization (RAII).
Ensuring proper disposal of acquired resources is actually a fairly common requirement in C++, and one it's well suited for. You can get object lifetime to manage resources for you using RAII, which has you place destruction/deallocation code in the destructor of the class, so it's automatically executed when the object managing the resource goes out of scope. This makes exception safety easy as well, since the destructor code is executed even in case of an exception.
In your case, you'd have a class store a pointer to the created SDL object, and then call the relevant SDL end-of-lifetime function for the variety of object you're managing in the destructor, so a texture management class might look like:
class Texture{
SDL_Texture* texture_;
public:
// or simply pass the arguments for texture creation in and create it internally
Texture(SDL_Texture* texture) : texture_{texture}{}
~Texture(){
if (texture_ != nullptr){
SDL_DestroyTexture(texture_);
}
}
// ... functions to access texture
};
Notice, though, that you're essentially wrapping a pointer to a resource with atypical end-of-lifetime requirements, and that those requirements are essentially to call a free function that takes the pointer as an argument, provided it's not a null pointer.
You don't need to make a specific class to do that--unique_ptr
was designed for this job and will do it admirably if you give it a custom deleter. Giving a unique_ptr
a custom deleter is an easy task, too.1
If you have a function that takes a pointer type T*
as its only argument and ends the lifetime of the pointed object and associated memory, it's as simple as this:
#include <memory>
void shoutFooDestruction(Foo* foo); // my special lifetime ending function
int main(){
std::unique_ptr<Foo, decltype(&shoutFooDestruction)> ptr{
new Foo(),
shoutFooDestruction};
ptr.reset(new Foo());
}
This will call shoutFooDestruction
twice: once for the original pointer given to the unique pointer at the start of its lifetime, and called when the unique_ptr
was reset
, and once for the Foo supplied on the reset, when the unique_pointer's lifetime ends at the end of main
.2
See it live on Coliru
The equivalent for an SDL_Texture would be:
std::unique_ptr<SDL_Texture, &SDL_DestroyTexture> message{
nullptr,
SDL_DestroyTexture};
and for an SDL_Surface:
std::unique_ptr<SDL_Surface, &SDL_FreeSurface> surfaceMessage{
nullptr,
SDL_FreeSurface};
You would then call reset
on the unique pointers when you wanted a new texture or surface from SDL, and they would dispose of any old texture or surface for you. Any remaining contents of the unique_ptr
s at the end of the function would be dealt with using the same functions when the unique_ptr
s went out of scope.
1. As seen in constructors 3 and 4 on cppreference's page on constructors for unique pointers
2. Calling reset
on a unique pointer with nullptr
for its value will not call the provided deleter and the same goes for unique_ptr
's destructor, so you don't need to worry about avoiding the deletion call if you don't have anything to delete.