【问题标题】:How to dynamically instantiate a new instance from type of another instance? c++如何从另一个实例的类型动态实例化一个新实例? C++
【发布时间】:2016-11-16 17:09:22
【问题描述】:

我想在堆上创建一个基于另一个对象的新对象,该对象的类型只能在运行时知道。

举个假设的例子,假设我们有一个游戏,用户选择一个角色,可以是巫师、战士或治疗师。计算机会创建一个匹配的非玩家角色来对抗玩家。也就是说,如果玩家选择了一个战士,计算机会生成另一个战士实例。

我正在尝试使用多态性。假设巫师、战士和治疗师都继承自“Combattype”类

我想做的是类似伪代码:

    combattype* player = new (chosen at runtimetype)();//player
    combattype* baseptr = new typeid(*player);   // computer - this doesn't work

我知道我可以使用类似的东西写一个 if 语句

if(typeid(player).name(*player) == typeid.name(warrior)) { // make warrior}

但这会很快变得复杂,有数百种类型。我觉得有更好的方法,但我无法从概念上提出。

我正在尝试做与这个问题类似的事情,但在 c++ 中:Dynamically create an object of <Type> 感谢您的帮助。

【问题讨论】:

  • 与其“尝试使用多态性”(读作:这是你的课堂作业,教授坚持使用多态性),不如使用那些对你有用且自然的机制?
  • 我不在课堂上。我正在学习来自 java 背景的 cpp,所以多态性对我来说很自然。 ;) 我不需要使用多态性,但我认为这可能是正确的方向;只需获取一个类型并基于该类型实例化一个新对象就可以了。

标签: c++ dynamic polymorphism instantiation decltype


【解决方案1】:

这(如果我理解正确的话)被称为 ¹克隆。只需在基类中添加一个虚拟的clone 成员函数即可。在每个具体派生类中覆盖它。


核心功能示例:

class Base
{
private:
    // Whatever
public:
    virtual auto clone() const
        -> Base*
    { return new Base( *this ); }

    virtual ~Base() {}
};

class Derived_A
    : public Base
{
public:
    auto clone() const
        -> Derived_A*        // OK, covariant return type.
        override
    { return new Derived_A( *this ); }
};

#include <assert.h>
#include <typeinfo>
auto main()
    -> int
{
    Base const& o = Derived_A{};
    auto p = o.clone();
    assert( typeid( *p ) == typeid( Derived_A ) );
    delete p;       // ← Manual cleanup is a problem with basic cloning.
}

用返回Derived_A* 而不是一般的Base* 的函数覆盖clone 函数是可以的,因为它是一个原始指针并且结果类型是协变的(在更具体的类中更具体,即在 与类特异性相同的方式)。对于原始参考,它也可以很好地工作。但是 C++ 不直接支持类类型函数结果,包括智能指针作为 clone 函数结果。

正如评论所指出的,直接简单克隆的一个问题是清理责任不明确。最好有自动化和有保证的,但随后就会遇到不支持协变智能指针的问题。令人高兴的是,可以“手动”实现协方差,方法是使虚拟 clone 函数成为非 public,并在每个类中提供特定于类的智能指针结果包装函数。


具有协变智能指针结果的克隆示例:

#include <memory>       // std::unique_ptr

class Base
{
private:
    // Whatever, and
    virtual auto virtual_clone() const
        -> Base*
    { return new Base( *this ); }

public:
    auto clone() const
    { return std::unique_ptr<Base>( virtual_clone() ); }

    virtual ~Base() {}
};

class Derived_A
    : public Base
{
private:
    auto virtual_clone() const
        -> Derived_A*        // OK, covariant return type.
        override
    { return new Derived_A( *this ); }

public:
    auto clone() const
    { return std::unique_ptr<Derived_A>( virtual_clone() ); }
};

#include <assert.h>
#include <typeinfo>
auto main()
    -> int
{
    Base const& o = Derived_A{};
    auto p = o.clone();
    assert( typeid( *p ) == typeid( Derived_A ) );
    // Automatic cleanup.
}

这是一种在BaseDerived_A 中自动生成克隆支持机制的方法,基于中间人继承的概念:

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

// Machinery:
template< class Derived_t >
class With_base_cloning_
{
private:
    auto virtual virtual_clone() const
        -> Derived_t*
    { return new Derived_t( *static_cast<Derived_t const*>( this ) ); }

public:
    auto clone() const
    { return std::unique_ptr<Derived_t>( virtual_clone() ); }

    virtual ~With_base_cloning_() {}
};

template< class Derived_t, class Base_t >
class With_cloning_
    : public Base_t
{
private:
    auto virtual_clone() const
        -> Base_t*          // Ungood type because Derived_t is incomplete here.
        override
    { return new Derived_t( *static_cast<Derived_t const*>( this ) ); }

public:
    auto clone() const
    { return std::unique_ptr<Derived_t>( static_cast<Derived_t*>( virtual_clone() ) ); }

    template< class... Args >
    With_cloning_( Args... args )
        : Base_t( std::forward<Args>( args )... )
    {}
};

你会这样使用它:

// Usage example:

class My_base
    : public With_base_cloning_<My_base>
{};

class Derived_A
    : public With_cloning_<Derived_A, My_base>
{};

#include <assert.h>
#include <typeinfo>
auto main()
    -> int
{
    My_base const& o = Derived_A{};
    auto p = o.clone();
    assert( typeid( *p ) == typeid( Derived_A ) );
    // Automatic cleanup.
}

With_cloning_ 中的(私有)virtual_clone 函数的返回类型并不是我们想要的,并不理想,因为在模板实例化的那一刻派生类还没有完成,所以编译器没有'还不知道它是从模板实例化派生的。

这种中间人继承解决方案的替代方案,包括一个简单的代码生成宏,以及在虚拟继承层次结构中的(复杂)支配地位。


¹ 一个clone 函数,在底部调用最派生类的复制构造函数,是virtual constructor idiom 的一种特殊情况。另一个特殊情况是create 函数,它在底部调用最派生类的默认构造函数。

【讨论】:

  • 为什么返回类型需要协变?他们不能都返回std::unique_ptr&lt;Base&gt;吗?
  • @BenjaminLindley:从技术上讲,他们都可以返回std::unique_ptr&lt;Base。但它需要static_cast 在每个静态已知类是派生类的地方,并且需要使用该派生类的功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-09
  • 1970-01-01
相关资源
最近更新 更多