问题
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: How to make boost::make_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<IInterface> Create();
virtual void Method() = 0;
};
class InterfaceImpl : public IInterface
{
public:
virtual void Method() {}
protected:
friend std::shared_ptr<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:
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;
}
回答2:
To the original question, std::make_shared<...>() does not directly instantiate your class, so providing friend access to it yields no benefit, as you found. You can simply provide friend access to the code that does directly use your protected constructor as follows:
friend class std::tr1::_Ref_count_obj<TheClassManagedByTheShared_Ptr>;
or in your case:
friend class std::tr1::_Ref_count_obj<InterfaceImpl>;
This works with the Microsoft compiler in VS2010, but it looks like it may be environment specific as it does not work with gcc on Linux. With gcc the std::tr1 namespace does not exist, so it must be specific to the Microsoft implementation of std library.
My normal working environment is the Intel 12.1 compiler, which appears to have a bug that does not check for access at all, and happily builds code without any friend declaration.
来源:https://stackoverflow.com/questions/3541632/using-make-shared-with-a-protected-constructor-abstract-interface