views:

51

answers:

1

Given an abstract interface and an implementation derived from that interface, where constructors are protected (creation of these objects only being available from a class factory - to implement a DI pattern), how can I make use of make_shared in the factory function?

For example:

class IInterface
{    
public:    
    virtual void Method() = 0;
};

class InterfaceImpl : public IInterface
{
public:
    virtual void Method() {}

protected:    
    InterfaceImpl() {}    
};

std::shared_ptr<IInterface> Create()
{
    std::shared_ptr<IInterface> object = std:: make_shared<InterfaceImpl>();    
    return object;
}

make_shared obviously cannot access the protected constructor in InterfaceImpl, or indeed in IInterface, giving me the following error


error C2248: 'InterfaceImpl::InterfaceImpl' : cannot access protected member declared in class 'InterfaceImpl'

So reading here (question: http://stackoverflow.com/questions/3378520/how-to-make-boostmake-shared-a-friend-of-my-class), I tried putting the following into the implementation class:


friend std::shared_ptr<InterfaceImpl> std::make_shared<InterfaceImpl>();

It still wouldn't compile. So then I put another one into the IInterface class too. Still no joy. What have I done wrong here?

EDIT: Full source file used to compile, with "friend"...

#include <memory>

class IInterface
{    
public:    
    friend std::shared_ptr&lt;IInterface> Create();     
    virtual void Method() = 0;
};

class InterfaceImpl : public IInterface
{    
public:     
    virtual void Method() {}

protected:    
    friend std::shared_ptr&lt;IInterface> Create();     
    InterfaceImpl() {}    
};

std::shared_ptr<IInterface> Create()
{
    std::shared_ptr<IInterface> object = std::make_shared<InterfaceImpl>();    
    return object;
}

void main()
{
    std::shared_ptr<IInterface> i = Create();   
}
+1  A: 

With VC10 the solution you linked to doesn't work - the construction of the instance of InterfaceImpl doesn't happen in make_shared, but in an internal type in std::tr1::_Ref_count_obj<Ty>::_Ref_count_obj(void).

I'd just make the Create() function a friend in your case and not use make_shared():

class InterfaceImpl : public IInterface {
// ...    
protected:
    friend std::shared_ptr<IInterface> Create();
    InterfaceImpl() {}
};

std::shared_ptr<IInterface> Create() {
    return std::shared_ptr<IInterface>(new InterfaceImpl());
}

... or use a custom make_shared() implementation that you actually can befriend without relying on ugly implementation details.

An alternative would be to use something like this pass-key-idiom:

class InterfaceImpl : public IInterface {
public:
    class Key {
        friend std::shared_ptr<IInterface> Create();
        Key() {}
    };
    InterfaceImpl(const Key&) {}
};

std::shared_ptr<IInterface> Create() {
    std::shared_ptr<IInterface> object = 
        std::make_shared<InterfaceImpl>(InterfaceImpl::Key());
    return object;
}
Georg Fritzsche
That doesn't compile either I'm afraid!
Robinson
@rob: Huh, i tested it in VC10 before posting. Are you testing with different code?
Georg Fritzsche
Maybe I missed something. Here's the code I'm compiling (follow up "answer" as the comment doesn't allow code it seems...!)...
Robinson
@rob: Don't post *"answers"* for such things, rather edit the question for updates.
Georg Fritzsche
Updated question to show full source I'm using.
Robinson
@rob: Your update still uses `make_shared()` - try copy/pasting the `Create()` from my answer.
Georg Fritzsche
Oh ok, yes, I see. That's what I'm doing at the moment. I was hoping to use make_shared instead of the standard shared_ptr construction, not just because it's "the great new thing to do", but because it may give me a little performance boost (cache coherence). I understand the arguments about premature optimisation. I just thought if I could do it, there's no harm in doing it. Obviously I can't do it!
Robinson
@rob: Ok, actually there is a way to use `make_shared()` if performance is a concern - updated the answer. I wonder why i didn't think of that earlier.
Georg Fritzsche
Yes, that works! I'll have to spend some time trying to figure out why it works though :p. Thanks Georg.
Robinson