Click here to Skip to main content
Click here to Skip to main content

No RAII to copy

By , 20 Jun 2010
 

A hidden danger of wrapping using RAII rears its head when we start copying the object around. Let's look at why and what we can do.

You may recall our RAII MIDI class from before:

class CMidiOut
{
public:
    CMidiOut()  :
        m_hMidiOutHandle(nullptr)
    {
        midiOutOpen(
            &m_hMidiOutHandle, 	// Handle
            MIDI_MAPPER,    	// Default device
            NULL,      		// No callback
            NULL,      		// No callback parameters
            CALLBACK_NULL);   	// Flags
    }
    ~CMidiOut()
    {
        if(m_hMidiOutHandle != nullptr)
        {
            midiOutClose(m_hMidiOutHandle);
            m_hMidiOutHandle = nullptr;
        }
    }
private:
    HMIDIOUT m_hMidiOutHandle;
}; 

Clients of the class no longer have to worry about the raw API calls, nor do they have to remember to close the handle and can write exception happy code. A danger lies in the compiler generated copy constructor though, and also the assignment operator. These default implementations will take a copy of our handle, which is fine, but as soon as one of the copies (or the original) is destroyed, there will be a call to midiOutClose. This is also okay, until one of the copies tries to use its handle. The handle has been closed. Our resource has been unacquired. Midi plays no more.

We would write defensive code to protect against this situation. We have two alternatives:

  1. Prevent copying of the RAII class.
  2. Use reference counting on duplicating the handle.

Of the two options, number 2 is by far the most interesting, but sadly we won't be using it here. DuplicateHandle does not work on HMIDIOUT and a reference count solution would work but is far too complicated for what we need. Instead, we are going to simply turn the class into a pure wrapper for HMIDIOUT and prevent any copies of it. This is done by making the copy constructor and assignment operator private:

class CMidiOutHandle
{
public:
    CMidiOutHandle();
    ~CMidiOutHandle();

private:
    CMidiOutHandle(const CMidiOutHandle &);
    CMidiOutHandle& operator=(const CMidiOutHandle&);

private:
    HMIDIOUT m_hMidiOutHandle;
}; 

Note also the name change. We are now making it clear that this class can be used in place of a HMIDIOUT (and it will, once we get to the accessor members).

Scott Meyer's in his excellent Effective C++ describes a reusable base class for copy prevention:

class Uncopyable
{
protected:
    Uncopyable();
    ~Uncopyable();

private:
    Uncopyable(const Uncopyable &);
    Uncopyable& operator=(const Uncopyable&);
};

There is also a noncopyable class in Boost, which does pretty much the same thing. You can then simply inherit this from your noncopyable classes:

class CMidiOutHandle : private Uncopyable

I do like the self documentation here, but I don't actually like to use the base class method. If you try and copy the object using the first method, it will error and helpfully point you to the line which is doing the copying. If inheriting from the non copyable base class, the compiler will say the error is caused by your class. Not a big deal, but enough for me to explicitly disallow the copy constructor and assignment operator.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

WebBiscuit
Software Developer Web Biscuit
United Kingdom United Kingdom
Member
At Web Biscuit, you can find software, articles, a good dollop of quality and an unhealthy obsession over biscuits.
Website: http://www.webbiscuit.co.uk
Twitter Watch: http://twitter.com/WebBiscuitCoUk

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
General"...and a reference count solution would work but is far too complicated for what we need..."memberpeterchen21 Jun '10 - 0:08 
GeneralRe: "...and a reference count solution would work but is far too complicated for what we need..."memberWebBiscuit21 Jun '10 - 8:21 
GeneralRe: "...and a reference count solution would work but is far too complicated for what we need..."memberAescleal24 Jun '10 - 20:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 20 Jun 2010
Article Copyright 2010 by WebBiscuit
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid