使用带有受保护构造函数和抽象接口的make_shared

5

假设有一个抽象接口和一个从该接口派生的实现,其中构造函数是受保护的(只能通过类工厂创建这些对象 - 以实现 DI 模式),我该如何在工厂函数中使用 make_shared?

例如:

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 显然无法访问 InterfaceImpl 中受保护的构造函数,或者在 IInterface 中也是如此,导致出现以下错误:


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

在阅读这篇文章时(问题:如何使boost::make_shared成为我的类的友元),我尝试将以下内容放入实现类中:


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

仍然无法编译。所以我也在IInterface类中添加了另一个。仍然没有成功。我在这里做错了什么?

编辑:使用“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();   
}

我猜那是VC10?顺便说一下,只要你和make_shared()交朋友,GCC就没有问题。 - Georg Fritzsche
这是VS2010,它实际上会给出一个警告(错误地——详见此处:http://connect.microsoft.com/VisualStudio/feedback/details/321690/c-vc9-bogus-warning-c4396-for-valid-code)。 - Robinson
2个回答

4
对于最初的问题,std::make_shared<...>()不会直接实例化您的类,因此提供友元访问权限没有任何好处,正如您所发现的那样。您可以按照以下方式简单地提供友元访问权限,以便直接使用受保护构造函数的代码:
friend class std::tr1::_Ref_count_obj<TheClassManagedByTheShared_Ptr>;

或者在您的情况下:
friend class std::tr1::_Ref_count_obj<InterfaceImpl>;

这在VS2010中使用Microsoft编译器可以工作,但看起来可能是与环境有关的,因为在Linux上使用gcc无法工作。在gcc中,std::tr1命名空间不存在,因此它必须是特定于Microsoft std库的实现。

我的正常工作环境是Intel 12.1编译器,它似乎存在一个错误,根本不检查访问权限,并且可以愉快地构建没有任何友元声明的代码。


使用VC2013工作过,即使我的类是模板化的。 - fmuecke

4
使用VC10编译器,您提供的解决方案无法正常工作 - make_shared中未构造InterfaceImpl实例,而是在std::tr1::_Ref_count_obj<Ty>::_Ref_count_obj(void)内部类型中构造。
在这种情况下,我建议将Create()函数设置为友元,并且不要使用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());
}

... 或者使用自定义的 make_shared() 实现,您可以通过友元关系而不是依赖于丑陋的实现细节来使用它。

另一种方法是使用类似于这个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;
}

很抱歉,那也无法编译! - Robinson
@rob:嗯,我在发布之前已经在VC10中测试过了。你是在测试不同的代码吗? - Georg Fritzsche
@rob:不要发布关于这些东西的“答案”,相反,请编辑问题进行更新。 - Georg Fritzsche
@rob:你的更新仍然使用make_shared() - 尝试复制/粘贴我的答案中的Create()函数。 - Georg Fritzsche
哦,好的,是的,我明白了。这就是我目前正在做的事情。我希望使用make_shared而不是标准的shared_ptr构造函数,不仅因为它是“新潮流”,而且因为它可能会给我一点性能提升(缓存一致性)。我理解关于过早优化的争论。我只是想如果我能做到,那么做一下也没有什么坏处。显然我现在不能做到! - Robinson
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接