使用shared_ptr的虚拟构造函数

时间:2014-12-21 03:34:50

标签: c++ design-patterns polymorphism smart-pointers

我感兴趣的是是否可以使用std::shared_ptr复制虚拟构造函数模式的行为(例如,参见virtual constructor example)。由共享指针替换原始指针的方法由于显而易见的原因(无效的协变返回类型)而失败。如果有人知道任何支持智能指针的替代方案,我感兴趣。

智能指针在项目的每个地方都使用,类似虚拟构造函数的方法似乎是我目前正在处理的问题的唯一可行方法。

代码如下:

class A
{

public:

    virtual std::shared_ptr<A> clone(void) const = 0;

    virtual void mymethod() const = 0;

};


class B : public A
{

    std::shared_ptr<B> clone(void) const
        {
            return (new B(*this));
        }

    void mymethod() const
        {
            std::cout << "type something";
        }

};

class C
{


public:

    void mymethod(std::shared_ptr<A const> MyB)
        {
            std::shared_ptr<A const> MyB2 = MyB -> clone();
            MyB2 -> mymethod();
        }

};

3 个答案:

答案 0 :(得分:4)

C ++的协变返回类型功能仅支持原始指针和引用。

支持克隆函数覆盖的单个实例很简单,但是将多个克隆函数覆盖实例集中在样板上更成问题。本质上,这是以通用方式表达协变函数实现的问题,C ++缺乏直接支持。该代码生成的可能解决方案包括

  • 宏。
    使用C ++ 03,这是明显的赢家,但是使用C ++ 11及更高版本时,它只是略微(并且可能是主观地)最好的。

  • 中间人继承混合 这在技术上可能在C ++ 03中,但在C ++ 11中转发构造函数参数变得非常简单。

  • 虚拟继承层次结构中的优势 一种复杂的技术,它依赖于某处不为人知的语言特征。

由于优势解决方案非常复杂且仅具有学术意义,因此下面仅举例说明(1)单个克隆覆盖的手动实现,(2)定义合适的宏,以及(3)中间人继承mixin。

单个克隆函数覆盖实例的手动实现示例:

// Manual implementation of a single clone function override.

#include <memory>

namespace my {
    using std::shared_ptr;

    class A
    {
    private:
        virtual auto virtual_clone() const
            -> A*
        { return new A( *this ); }

    public:
        auto clone() const
            -> shared_ptr<A>
        { return shared_ptr<A>( virtual_clone() ); }

        virtual void m() const {}
    };

    class B
        : public A
    {
    private:
        auto virtual_clone() const
            -> B* override
        { return new B( *this ); }

    public:
        auto clone() const
            -> shared_ptr<B> 
        { return shared_ptr<B>( virtual_clone() ); }
    };
}  // namespace my

void foo( std::shared_ptr<my::A const> b )
{
    std::shared_ptr<my::A const> b2 = b->clone();
    b2->m();
}

auto main()
    -> int
{ foo( std::shared_ptr<my::A>( new my::B ) ); }

宏作为一般解决方案的示例:

// Macro-based centralization of covariant boilerplate code.

#include <memory>

#define MY_CLONE_IMPL( classname )          \
    private:                                \
        virtual auto virtual_clone() const  \
            -> classname*                   \
        { return new classname( *this ); }  \
    public:                                 \
        auto clone() const                  \
            -> std::shared_ptr<classname>   \
        { return std::shared_ptr<classname>( virtual_clone() ); }

void say( char const* );

namespace my {
    using std::shared_ptr;

    class A
    {
        MY_CLONE_IMPL( A )
    public:
        virtual void m() const { say( "A::m" ); }
    };

    class B
        : public A
    {
        MY_CLONE_IMPL( B )
    public:
        virtual void m() const { say( "B::m" ); }
    };
}  // namespace my

void foo( std::shared_ptr<my::A const> b )
{
    std::shared_ptr<my::A const> b2 = b->clone();
    b2->m();
}

#include <iostream>
void say( char const* s ) { std::cout << s << "\n"; }

auto main()
    -> int
{ foo( std::shared_ptr<my::A>( new my::B ) ); }

作为一般解决方案的中间人继承混合的例子:

// Middle-man mixin centralization of covariant boilerplate code.

#include <memory>       // std::shared_ptr
#include <utility>      // std::forward

struct Void {};

template< class Derived, class Base >
class With_cloning_of_
    : public Base
{
private:                              
    virtual auto virtual_clone() const
        -> Base*
    { return new Derived( static_cast<Derived const&>( *this ) ); }

public:                               
    auto clone() const                
        -> std::shared_ptr<Derived> 
    {
        return std::shared_ptr<Derived>(
            static_cast<Derived*>( virtual_clone() )
            );
    }

    template< class... Args >
    With_cloning_of_( Args&&... args )
        : Base( std::forward<Args>( args )... )
    {}
};

void say( char const* );

namespace my {
    using std::shared_ptr;

    class A
        : public With_cloning_of_<A, Void>
    {
    public:
        virtual void m() const { say( "A::m" ); }
    };

    class B
        : public With_cloning_of_<B, A>
    {
    public:
        virtual void m() const { say( "B::m" ); }
    };
}  // namespace my

void foo( std::shared_ptr<my::A const> b )
{
    std::shared_ptr<my::A const> b2 = b->clone();
    b2->m();
}

#include <iostream>
void say( char const* s ) { std::cout << s << "\n"; }

auto main()
    -> int
{ foo( std::shared_ptr<my::A>( new my::B ) ); }

如果您想使用 std::make_shared 进行克隆,那么这会让事情变得复杂。

一种方法,即不引入像shared_from_this那样的其他状态,就是让虚拟克隆函数将shared_ptr返回给已知的共同祖先。

其中一个主要问题是引入了一定程度的自由,引入了错误的自由,但这仍然是C ++中经常做的事情,所以我给出了这个例子:

// std::make_shared with middle-man mixin.

#include <memory>       // std::shared_ptr
#include <utility>      // std::forward

struct Void {};

template< class Derived, class Base, class Common_ancestor >
class With_cloning_of_
    : public Base
{
private:
    virtual auto virtual_clone() const
        -> std::shared_ptr<Common_ancestor>
    { return std::make_shared<Derived>( static_cast<Derived const&>( *this ) ); }

public:                               
    auto clone() const                
        -> std::shared_ptr<Derived> 
    { return std::static_pointer_cast<Derived>( virtual_clone() ); }

    template< class... Args >
    With_cloning_of_( Args&&... args )
        : Base( std::forward<Args>( args )... )
    {}
};

void say( char const* );

namespace my {
    using std::shared_ptr;

    class A
        : public With_cloning_of_<A, Void, Void>
    {
    public:
        virtual void m() const { say( "A::m" ); }
    };

    class B
        : public With_cloning_of_<B, A, Void>
    {
    public:
        virtual void m() const { say( "B::m" ); }
    };
}  // namespace my

void foo( std::shared_ptr<my::A const> b )
{
    std::shared_ptr<my::A const> b2 = b->clone();
    b2->m();
}

#include <iostream>
void say( char const* s ) { std::cout << s << "\n"; }

auto main()
    -> int
{ foo( std::shared_ptr<my::A>( new my::B ) ); }

答案 1 :(得分:1)

你总是可以假装协变返回类型。

struct A {
  virtual shared_ptr<A> getA();
  shared_ptr<A> get() {
    return getA();
  }
};

struct B : A {
  virtual shared_ptr<B> getB();
  shared_ptr<B> get() {
    return getB();
  }
  shared_ptr<A> getA() {
    return getB();
  }
};

答案 2 :(得分:0)

不幸的是,C ++并没有真正为此设计好。一种选择是在http://lists.boost.org/boost-users/2003/02/2996.php做一些类似Peter Dimov的技巧。在某些应用程序中,只需让B的克隆返回shared_ptr&lt; A&gt;就可以完全避免这种情况。 (我意识到这是一种作弊行为,这就是我开始使用Peter Dimov更全面的解决方案的原因,但有时候最好完全避免这个问题!)