The Copy&Swap Idiom

Started by
31 comments, last by TheComet 10 years, 6 months ago

Is this not how it's done traditionally? Or am I missing something?


'traditional' isn't a technical term, but in this context it would mean 'plainly wrong and rather straw-mannish'. Of course you can get leakage if you do it that way. You're using raw pointers for resource control. That doesn't mean that C&S is the only alternative. Doing it right is another alternative, as I showed in the block after that.

C&S collects the copying behavior and then adopts it in a single transaction. That's the correct pattern, but C&S is not the only way to implement that pattern, and it can be unclear, make the machine do more work than it needs to, and/or trigger side-effects.

Using smart pointers or other RAII implementors allows you to prepare the new state, and then once everything is created successfully you adopt it. That's the 'normal' way of writing exception safe code. C&S is just a trick for doing that by invoking the copy ctor and then adopting it only if construction succeeds.

It does not magically guarantee exception safety.

Your code here is NOT exception safe!


    // copy constructor
    Player( const Player& that ) : m_Texture( new Texture(*that.m_Texture) ),
                                                  m_Sprite( new Sprite(*that.m_Texture) ),
                                                  m_Health( new m_HealthController(*that.m_Health) )

    {
    }

What if the allocation for m_Health fails? m_Texture and m_Sprite have already been allocated! How will you reach them in order to release them, since the object failed construction?

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.
Advertisement

Your code here is NOT exception safe!


    // copy constructor
    Player( const Player& that ) : m_Texture( new Texture(*that.m_Texture) ),
                                                  m_Sprite( new Sprite(*that.m_Texture) ),
                                                  m_Health( new m_HealthController(*that.m_Health) )

    {
    }
What if the allocation for m_Health fails? m_Texture and m_Sprite have already been allocated! How will you reach them in order to release them, since the object failed construction?

Code like this shows why RAII works best when there is only one resource per object. If you wrap each of m_Texture, m_Sprite, and m_Health in it own object, then these sorts of problems go away. Alternatively, proper use of smart pointers used together with make_shared and the like resolves the problems too, because of how make_shared works.

Not that it's not possible to write a class that maintains two resources and is exception safe, but that's doing things the hard way, and is a likely spot for bugs introduced when the code is later maintained.

@Khatharr & King Mir - Ah, I see now. Thanks for the pointers!

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty

This topic is closed to new replies.

Advertisement