简体   繁体   中英

RAII understanding - Accesing methods of a bounded pointer

I was reading about the RAII concept and while attempting to understand it , I formulated the following example.

class foo_handler
{
private:
 foo* f;
public:
  foo_handler(foo* inc) : f(inc){}
  ~foo_handler(){delete f;}
};

My question is that suppose foo has a method called SomeMethod doesnt foo_handler need to have a method that has the following signature

foo* returnFoo();

so that we can access the exposed methods of foo ? I was a bit confused here since this link , the answer by the the_mandrill did not explain this scenario

so this could be used as

foo_handler d;
d.returnFoo()->SomeMethod();

First of all, there is a (widespread) confusion, so let me clear it.


RAII stands for Resources Acquisition Is Initialization. The concern here is one of class invariants , and tying the acquisition of the resource to the construction of the object means that: whenever you have an handle on such an object, it is backed by a resource .

Or put another way, if acquiring the resource fail, then the object is never born (its constructor aborts/throws/whatever).

However, because typically RAII containers will also take care of releasing the resource upon their destruction, RAII is often colloquially used to refer to proper resource management. This is beyond its scope though, and a container such as:

struct RaiiHandle {
    RaiiHandle(Handle& h): handle(h) {}

    Handle& handle;
};

which does nothing about resource management is still a RAII container as it guarantees the presence of the resource.

Other acronyms have been tried to express the proper management of resources, such as SBRM: Scope Bound Resource Management, however they did not take (in SBRM case, it fails to account for the fact that a resource is not necessarily tied to a lexical scope).


A word of caution on your interface.

In the days of C, there was a piece of wisdom that said that whoever allocated a resource (often memory) should also provide a method to deallocate it. This ensured that the resource was properly deallocated because only its creator knows how it was allocated. Even today, it still remains a good advice.

Your interface, however, suffers from asymmetry: you are passed a pointer and call delete on it. But what if it was a pointer to an array ? What if it was a stack object ? Calling delete is preposterous.


A word of caution on your implementation.

I hope for your sake this is a trimmed down example, however since they are missing I cannot help but remind you that you should follow the Rule of Three (make it Five in C++11).

A very simple way never to forget is to change your raw pointer for a unique_ptr (possibly with a custom deleter). It will have correct move members by default and will force you to write the copy members should you need them.


Back to your question: Yes in general you will need a way to access the underlying resource, otherwise it would be useless.

There are multiple ways though:

  • if your container is specialized, it may present a domain oriented interface, this is what fstream does for example: you do not access the FILE* or whatever is inside, but instead use << and >> and .eof() , etc..
  • you may return a handle (pointer or reference)
  • you may have a method that takes a functor and will call it with a reference to the object (more functional oriented, less seen in C++)

It is up to you to design the interface so that your clients may actually use the resource.

C++ doesn't have a try-finally construct and RAII is basically a simulation of the finally block with the toolset of C++ (well, maybe a bit more than that but its quite easy to understand it this way if you know try-finally). You write a class with a destructor and you instantiate this class by value on your stack. When this instance runs out of the scope its destructor gets executed no matter how you leave the scope (with return, exception, break, continue, ...). This destructor execution can be treated as a "finally" block, so you can put the cleanup of your resources into the destructor to make sure that the resources don't leak at the end of the scope (In this case your foo*). In C++ there are also some special scopes (when you define an instance of your RAII-based class by value as part of another object - then the scope of your RAII object is the lifetime of its container object, this doesn't really fit with the try-finally example...)

This explanation is of course meaningful to you only if you understand the concept of the try-finally blocks from other languages... Visual C++ also had a nonstandard __finally handler (SEH based), I don't know whether it still has this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM