2010-09-07 3 views
7

я видел, что полезный способ, чтобы написать метод клонирования, который возвращает повышение :: shared_ptr это сделатьКак сделать метод клонирования с помощью shared_ptr и наследующий enable_shared_from_this

class A 
{ 
public: 
    shared_ptr<A> Clone() const 
    { 
    return(shared_ptr<A>(CloneImpl())); 
    } 
protected: 
    virtual A* CloneImpl() const 
    { 
    return(new A(*this)); 
    } 
}; 

class B : public A 
{ 
public: 
    shared_ptr<B> Clone() const 
    { 
    return(shared_ptr<B>(CloneImpl())); 
    } 
protected: 
    virtual B* CloneImpl() const 
    { 
    return(new B(*this)); 
    } 
}; 

Это позволяет использовать ковариации с регулярным указателем, все еще обертывая его безопасностью умного указателя. Моя проблема заключается в том, что мой класс B должен наследовать от boost :: enable_shared_from_this, потому что сразу после построения ему необходимо зарегистрировать себя с отдельным классом, передав общий указатель на себя. У меня есть метод Create, который завершает создание и регистрацию, чтобы убедиться, что они всегда встречаются вместе. Однако описанная выше реализация метода clone не может справиться с этим требованием. Регистрация не может произойти в CloneImpl, так как no shared_ptr еще не существует, «владея» объектом, предотвращая вызов shared_from_this(), и если эта логика не находится в виртуальной функции, то shared_ptr, указывающий на B, не знает о требованиях регистрации B при клонировании. Каков наилучший способ справиться с этой проблемой?

+0

Скомпилирует ли этот код? Похож B :: Clone() скрывает A :: Clone(). –

+0

Этот пример составлен для меня. Я считаю, что целесообразна, что B :: Clone() скрывает A :: Clone(), чтобы получить правильный тип возврата. – user334066

ответ

7

Поскольку вы уже реализует интерфейс ковариации общественного самостоятельно через невиртуальных Clone() функций, вы можете рассмотреть возможность отказа от ковариации для CloneImpl() функций.

Если вам нужно только shared_ptr и никогда не сырой указатель, так что вы могли бы сделать:

class X 
{ 
public: 
    shared_ptr<X> Clone() const 
    { 
    return CloneImpl(); 
    } 
private: 
    virtual shared_ptr<X> CloneImpl() const 
    { 
    return(shared_ptr<X>(new X(*this))); 
    } 
}; 

class Y : public X 
{ 
public: 
    shared_ptr<Y> Clone() const 
    { 
    return(static_pointer_cast<Y, X>(CloneImpl())); // no need for dynamic_pointer_cast 
    } 
private: 
    virtual shared_ptr<X> CloneImpl() const 
    { 
    return shared_ptr<Y>(new Y(*this)); 
    } 
}; 

CloneImpl() всегда будет возвращать shared_ptr<Base> и теперь вы можете зарегистрировать свой объект внутри функции B::CloneImpl() и вернуть Registerd shared_ptr ,

+0

Спасибо, я считаю, что это очень хорошо решает проблему. Фактически, даже игнорируя требование регистрации, разве этот метод клонирования не выглядит лучше, чем тот, который я перечислял, что является примером, который я всегда вижу при клонировании с помощью интеллектуальных указателей? Эта версия, похоже, дает ту же функциональность, что и удаление любого возможного неправильного использования обычного указателя, созданного в списке CloneImpl I. – user334066

+0

@ user334066 - Думаю, все зависит. Пример, который я опубликовал, можно найти в архивах списков пересылки. Функция «Клонировать» допускает внутреннее использование класса с необработанным указателем, которое может время от времени иметь смысл. –