2
I’m new to C++ and recently came across something that I can’t quite understand, which is: in my research, I saw that RAII is a suitable technique to be used when you need to acquire a resource in the constructor and destroy automatically in the destructor.
In these same researches, I also learned that RAII is widely used with exceptions and it is often stated that exceptions are the main way to deal with errors in constructors since they do not return value.
However, in my research involving a dozen sites and a few books, I did not see an example involving a RAII, in which the builder made an exception. And when I tried to do it myself, things just didn’t work out.
Below, my attempts (I will use as an example resource acquired in the builder the pointer to the struct SDL_Surface*
of the SDL library, which I am most familiar with):
#include <SDL.h>
#include <SDL_image.h>
#include <iostream>
class Surface {
private:
SDL_Surface* surface_;
public:
Surface(std::string path) {
surface_ = IMG_Load(path.c_str());
if (!surface_) {
throw std::runtime_error("unable to load file.\n");
}
}
~Surface() {
if (surface_) {
SDL_FreeSurface(surface_);
}
}
SDL_Surface* getSurface() {
return surface_;
}
};
int main(int argc, char* args[]) {
SDL_Init(SDL_INIT_VIDEO);
std::string file_path = "foo.png";
try {
Surface foo_surface(file_path);
}
catch (std::runtime_error &e) {
std::cerr << "Caught a runtime_error exception: "
<< e.what() << '\n';
}
//agora ao tentar usar foo_surface, não é possível pois a variável saiu do escopo ao
//final do bloco try
int largura = foo_surface.getSurface()->h;
int altura = foo_surface.getSurface()->w;
return 0;
}
I mean, on this first attempt, things didn’t work out because foo_surface
lost scope at the end of the block try{}
and, of course, the compiler complains: “identifier ‘foo_surface’ is undefined”
.
To try to circumvent this, a second attempt with some modifications (displayed only the function main()
):
int main(int argc, char* args[]) {
SDL_Init(SDL_INIT_VIDEO);
std::string file_path = "foo.png";
Surface* foo_surface = nullptr;
try {
foo_surface = new Surface("foo.png");
}
catch (std::runtime_error &e) {
std::cerr << "Caught a runtime_error exception: "
<< e.what() << '\n';
return -1;
}
//até aqui as coisas funcionam bem, mas...
int largura = foo_surface->getSurface()->h;
int altura = foo_surface->getSurface()->w;
//eu preciso deletar foo_surface no final do escopo.
delete foo_surface;
return 0;
}
The code works (and compiles), but the problem is that now I need to delete foo_surface
at the end of the scope. And why is that a problem? Like, the only reason I created a class was the possibility of the destructor automatically releasing the memory when the object lost the scope, with the need to delete the object I lose my original motivation and obviously if to use delete, it is much easier to simply abandon automatic memory management and stick to the solution without using class, constructor, destructor and exception, which consists of:
int main(int argc, char* args[]) {
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface* foo_surface = IMG_Load("foo.png");
if (!foo_surface) {
std::cerr << "Unable to load image.\n";
return -1;
}
int largura = foo_surface ->w;
int altura = foo_surface->h;
SDL_FreeSurface(foo_surface);
return 0;
}
Anyway, as I read a lot that RAII and exceptions are commonly used together, as I saw no practical example of this, and as my attempt to use both things together simply did not work as stated above, the impression I have is that I’m letting something very basic escape.
So my doubts are:
- my approaches were correct or I’m making a very big mistake of beginner?
- And how to reconcile the automatic memory management provided by RAII with exceptions released by the constructor, given the block scope problem
try{}
;
Finally, I’m not a programmer, in fact, I started studying C++ about a year ago, out of simple curiosity and (I loved programming), so I apologize for the numerous conceptual, terminological and coding errors I must have made in this question.
Got it. Your answer makes perfect sense and solved other problems. I mean, even in the second example the only treatment I could give for the exception was the closure of the program, otherwise, foo_surface could not have been initialized and an error would occur on the line
int largura = foo_surface ->w;
and on the next line. Well, I accepted the answer, I think I understood what I was missing, I gave upvote too, but it doesn’t show up because I don’t have a reputation for it. Vlw.– user142154