Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ RAII Questions

Tags:

c++

windows

raii

So as I understand it to implement RAII properly, if I where to call CreateFont, I'd wrap that in a class with CreateFont in the constructor and DeleteObject in the destructor, so it cleans it up when it goes out of scope.

First question is, won't I end up with ALOT of classes doing that? Especially since the class only has a constructor and destructor.

Second question is, what if I'm calling the CreateFont class in the WndProc, that goes out of scope constantly. So am I supposed to do all my calls to CreateFont or like LoadBitmap in the WndMain? I'm used to calling those functions in WM_CREATE and cleaning them up in WM_DESTROY.

like image 921
Josh Avatar asked Sep 26 '11 14:09

Josh


People also ask

Does C have RAII?

C does not have RAII-like memory management in any way. Exceptions work beautifully with memory management like that, but if it's not there, you can't just say it should work because memory management should work like that.

What does RAII mean in C++?

The principle that objects own resources is also known as "resource acquisition is initialization," or RAII. When a resource-owning stack object goes out of scope, its destructor is automatically invoked. In this way, garbage collection in C++ is closely related to object lifetime, and is deterministic.

Are smart pointers necessary for RAII?

Smart pointers will use the RAII philosophy to ensure that heap allocated objects are destroyed any time the pointer variable is destroyed. In addition - pointers are the most common application of RAII - you'll likely allocate thousands of times more pointers than any other resource.

What does RAII do?

Resource Acquisition Is Initialization or RAII, is a C++ programming technique which binds the life cycle of a resource that must be acquired before use (allocated heap memory, thread of execution, open socket, open file, locked mutex, disk space, database connection—anything that exists in limited supply) to the ...


3 Answers

You can avoid a lot of repetitious work by using a template to help you. For example if you use boost::shared_ptr you can do:

#include <boost/shared_ptr.hpp>
#include <functional>

struct Font;

Font *createFont();
void deleteFont(Font*);

int main() {    
  boost::shared_ptr<Font> font(createFont(), std::ptr_fun(deleteFont));
}

Which saves you writing a custom class to manage the resource. If boost and TR1 or newer aren't available to you it's still possible to implement something similar and generic yourself to assist.

boost::shared_ptr is reference counted properly, so if you want to create it somewhere and "promote" it to live longer later you can do so by copying it somewhere longer lived before it dies.

like image 88
Flexo Avatar answered Oct 21 '22 05:10

Flexo


First question is, won't I end up with ALOT of classes doing that? Especially since the class only has a constructor and destructor.

Yes, but there are few points to consider:

  • is it a problem? The classes will be small and easy to read and understand,
  • you might be able to reuse many of them (for example, there are a lot of Win32 functions which create HANDLE objects, and they're all closed the same way (with CloseHandle), so you could reuse the same class for those.
  • you can use a smart pointer or some other generic wrapper to fill in most of the boilerplate code. The most popular smart pointer classes allow you to specify a custom deleter function.

Second question is, what if I'm calling the CreateFont class in the WndProc, that goes out of scope constantly.

Store it in a location where it won't go out of scope prematurely. :) Here, smart pointers might be useful again. For example, shared_ptr could be used to keep the font alive as long as there's at least one shared_ptr pointing to it. Then you can simply pass it out of the function to some common longer-lived location.

Otherwise, as long as you implement copy constructor and assignment operator (in C++11 you might want to implement move constructor and move assignment instead) for your RAII class, it can be copied (or moved) safely to wherever you want to put it, even if it was created in a smaller scope.

like image 33
jalf Avatar answered Oct 21 '22 04:10

jalf


First question is, won't I end up with ALOT of classes doing that? Especialy since the class only has a constructor and deconstructor

If you don't like the number of classes you'd need to create for each different type of object, you can create a single RAII class that takes a HGDIOBJ parameter in the constructor and calls DeleteObject in the destructor. This class can then be used for all the different GDI objects. For example:

class GDIObject
{
public:
    HGDIOBJ GdiObject;

    GDIObject( HGDIOBJ object )
        : GdiObject( object )
    {
    }

    ~GDIObject()
    {
        DeleteObject( GdiObject );
    }
}

...

GDIObject font( CreateFont( 48, 0, 0, 0, FW_DONTCARE, false, true, false, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH, TEXT("Impact") ) );

Second question is, what if I'm calling the CreateFont class in the WndProc, that goes out of scope constantly. So am i supposed to do all my calls to CreateFont or like LoadBitmap in the WndMain? I'm used to calling those functions in WM_CREATE and cleaning them up in WM_DESTROY.

For items that need to remain in-memory for longer than the function scope, you'll have to put these at the global level.

like image 45
Jon Benedicto Avatar answered Oct 21 '22 05:10

Jon Benedicto