[英]Virtual constructor with shared_ptr
我對是否可以使用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();
}
};
C ++的協變返回類型功能僅支持原始指針和引用。
支持克隆功能覆蓋的單個實例很簡單,但集中多個副本功能覆蓋的實例的樣板就更成問題了。 本質上,這就是以通用方式表示協變函數實現的問題,而C ++對此缺乏直接支持。 該代碼生成的可能解決方案包括
宏。
對於C ++ 03,這是明顯的贏家,但是對於C ++ 11及更高版本,它僅是次要的(可能是主觀的)最好的。
一個中間人的繼承mixin。
從技術上講,這可能在C ++ 03中實現,但是構造函數參數的轉發在C ++ 11中變得極為簡單。
虛擬繼承層次結構中的優勢。
一種復雜的技術,它依賴於某處不知名的語言功能。
由於支配性解決方案非常復雜且僅具有學術意義,因此在下文中,我僅舉例說明(1)手動實現單個克隆替代,(2)定義合適的宏以及(3)中間人繼承混合。
手動實現克隆功能覆蓋的單個實例的示例:
// 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 ) ); }
中間人繼承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 ) ); }
如果您想使用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 ) ); }
您始終可以偽造協變量返回類型。
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();
}
};
不幸的是,C ++並不是為此精心設計的。 一種選擇是在http://lists.boost.org/boost-users/2003/02/2996.php上執行Peter Dimov的技術。 在某些應用程序中,只需讓B的克隆返回shared_ptr <A>,就可以完全避免這種情況。 (我意識到這是一種作弊,這就是為什么我從彼得·迪莫夫(Peter Dimov)的更全面解決方案開始的原因,但是有時最好完全避免該問題!)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.