RAII and smart pointers in C++

后端 未结 6 1547
走了就别回头了
走了就别回头了 2020-11-21 23:45

In practice with C++, what is RAII, what are smart pointers, how are these implemented in a program and what are the benefits of using RAII with smart pointers?

6条回答
  •  佛祖请我去吃肉
    2020-11-22 00:28

    RAII This is a strange name for a simple but awesome concept. Better is the name Scope Bound Resource Management (SBRM). The idea is that often you happen to allocate resources at the begin of a block, and need to release it at the exit of a block. Exiting the block can happen by normal flow control, jumping out of it, and even by an exception. To cover all these cases, the code becomes more complicated and redundant.

    Just an example doing it without SBRM:

    void o_really() {
         resource * r = allocate_resource();
         try {
             // something, which could throw. ...
         } catch(...) {
             deallocate_resource(r);
             throw;
         }
         if(...) { return; } // oops, forgot to deallocate
         deallocate_resource(r);
    }
    

    As you see there are many ways we can get pwned. The idea is that we encapsulate the resource management into a class. Initialization of its object acquires the resource ("Resource Acquisition Is Initialization"). At the time we exit the block (block scope), the resource is freed again.

    struct resource_holder {
        resource_holder() {
            r = allocate_resource();
        }
        ~resource_holder() {
            deallocate_resource(r);
        }
        resource * r;
    };
    
    void o_really() {
         resource_holder r;
         // something, which could throw. ...
         if(...) { return; }
    }
    

    That is nice if you have got classes of their own which are not solely for the purpose of allocating/deallocating resources. Allocation would just be an additional concern to get their job done. But as soon as you just want to allocate/deallocate resources, the above becomes unhandy. You have to write a wrapping class for every sort of resource you acquire. To ease that, smart pointers allow you to automate that process:

    shared_ptr create_entry(Parameters p) {
        shared_ptr e(Entry::createEntry(p), &Entry::freeEntry);
        return e;
    }
    

    Normally, smart pointers are thin wrappers around new / delete that just happen to call delete when the resource they own goes out of scope. Some smart pointers, like shared_ptr allow you to tell them a so-called deleter, which is used instead of delete. That allows you, for instance, to manage window handles, regular expression resources and other arbitrary stuff, as long as you tell shared_ptr about the right deleter.

    There are different smart pointers for different purposes:

    unique_ptr

    is a smart pointer which owns an object exclusively. It's not in boost, but it will likely appear in the next C++ Standard. It's non-copyable but supports transfer-of-ownership. Some example code (next C++):

    Code:

    unique_ptr p(new plot_src); // now, p owns
    unique_ptr u(move(p)); // now, u owns, p owns nothing.
    unique_ptr v(u); // error, trying to copy u
    
    vector> pv; 
    pv.emplace_back(new plot_src); 
    pv.emplace_back(new plot_src);
    

    Unlike auto_ptr, unique_ptr can be put into a container, because containers will be able to hold non-copyable (but movable) types, like streams and unique_ptr too.

    scoped_ptr

    is a boost smart pointer which is neither copyable nor movable. It's the perfect thing to be used when you want to make sure pointers are deleted when going out of scope.

    Code:

    void do_something() {
        scoped_ptr sp(new pipe);
        // do something here...
    } // when going out of scope, sp will delete the pointer automatically. 
    

    shared_ptr

    is for shared ownership. Therefor, it's both copyable and movable. Multiple smart pointer instances can own the same resource. As soon as the last smart pointer owning the resource goes out of scope, the resource will be freed. Some real world example of one of my projects:

    Code:

    shared_ptr p(new plot_src(&fx));
    plot1->add(p)->setColor("#00FF00");
    plot2->add(p)->setColor("#FF0000");
    // if p now goes out of scope, the src won't be freed, as both plot1 and 
    // plot2 both still have references. 
    

    As you see, the plot-source (function fx) is shared, but each one has a separate entry, on which we set the color. There is a weak_ptr class which is used when code needs to refer to the resource owned by a smart pointer, but doesn't need to own the resource. Instead of passing a raw pointer, you should then create a weak_ptr. It will throw an exception when it notices you try to access the resource by an weak_ptr access path, even though there is no shared_ptr anymore owning the resource.

提交回复
热议问题