带shared_ptr的虚拟构造函数

3 c++ polymorphism design-patterns smart-pointers

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

项目中到处都使用智能指针,类似虚拟构造函数的方法似乎是解决我当前正在解决的问题的唯一方法。

代码如下:

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

};
Run Code Online (Sandbox Code Playgroud)

Che*_*Alf 5

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 ) ); }
Run Code Online (Sandbox Code Playgroud)

作为通用解决方案的宏示例:

// 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 ) ); }
Run Code Online (Sandbox Code Playgroud)

中间人继承 mixin 作为通用解决方案的示例:

// 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 ) ); }
Run Code Online (Sandbox Code Playgroud)

如果您想用于std::make_shared克隆,那么这会使事情变得有点复杂。

一种不会像以前那样引入额外状态的方法shared_from_this是让虚拟克隆函数将 a 返回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 ) ); }
Run Code Online (Sandbox Code Playgroud)