Использование make_shared с защищенным конструктором + абстрактный интерфейс - PullRequest
5 голосов
/ 22 августа 2010

Учитывая абстрактный интерфейс и реализацию, производную от этого интерфейса, где конструкторы защищены (создание этих объектов доступно только из фабрики классов - для реализации шаблона 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. Все еще нет радости. Что я здесь не так сделал?

РЕДАКТИРОВАТЬ: полный исходный файл, используемый для компиляции, с «другом» ...

#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();   
}

Ответы [ 2 ]

4 голосов
/ 12 декабря 2012

На первоначальный вопрос std :: make_shared <...> () не создает непосредственно экземпляр вашего класса, поэтому, как вы обнаружили, предоставление доступа к нему для друзей не приносит никакой пользы. Вы можете просто предоставить другу доступ к коду, который напрямую использует ваш защищенный конструктор, следующим образом:

friend class std::tr1::_Ref_count_obj<TheClassManagedByTheShared_Ptr>;

или в вашем случае:

friend class std::tr1::_Ref_count_obj<InterfaceImpl>;

Это работает с компилятором Microsoft в VS2010, но похоже, что оно может зависеть от среды, поскольку не работает с gcc в Linux. С gcc пространство имен std :: tr1 не существует, поэтому оно должно быть специфичным для реализации библиотеки std от Microsoft.

Моя обычная рабочая среда - это компилятор Intel 12.1, в котором, как представляется, есть ошибка, которая вообще не проверяет доступ и успешно создает код без объявления друга.

4 голосов
/ 22 августа 2010

С VC10 решение, с которым вы связаны, не работает - создание экземпляра InterfaceImpl происходит не в make_shared, а во внутреннем типе в std::tr1::_Ref_count_obj<Ty>::_Ref_count_obj(void).

Iпросто сделайте функцию Create() friend в вашем случае и не используйте 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;
}
...