繁体   English   中英

当子对象存储在基本指针的向量上时,如何动态地从基类转换为子类

[英]How to dynamic cast from base to child class when the child is stored on a vector of base pointers

我已经将Base对象的共享指针存储在Child对象的共享指针中,并且我需要将Base向量的元素动态转换为其Child类型 ,以便可以调用具有Child特定签名的函数

下面是一个示例。 第一个代码块定义了类层次结构以及我要使用的“标识”功能。 假设我可以将原始对象类型从Base类转换为Child类(例如A,B,C),则第二个代码块给出了如何调用TYPE特定的“ identify”函数的特定示例。

有什么方法或技术可以解决这个问题?

#include <iostream>
#include <memory>
#include <vector>

class Base {};
class A : public Base{};
class B : public Base{};
class C : public Base{};

class CollectionOfBase
{
public:
    void add (std::shared_ptr<Base> item){m_items.push_back(item);}
    std::vector<std::shared_ptr<Base>> const& getItems() const {return m_items;}

private:
    std::vector<std::shared_ptr<Base>> m_items;
};

// I want to use these 3 functions instead of identify( std::shared_ptr<Base> const& )
void identify( std::shared_ptr<A> const& )
{
    std::cout << "A" << std::endl;
}
void identify( std::shared_ptr<B> const& )
{
    std::cout << "B" << std::endl;
}
void identify( std::shared_ptr<C> const& )
{
    std::cout << "C" << std::endl;
}

//This function works in the below for loop, but this is not what I need to use
void identify( std::shared_ptr<Base> const& )
{
    std::cout << "Base" << std::endl;
}

在下面,您可以找到第二个代码块:

int main()
{
    using namespace std;

    CollectionOfBase collection;

    collection.add(make_shared<A>());
    collection.add(make_shared<A>());
    collection.add(make_shared<C>());
    collection.add(make_shared<B>());

    for (auto const& x : collection.getItems())
    {
        // THE QUESTION:
        // How to distinguish different type of items
        // to invoke "identify" with object specific signatures (e.g. A,B,C) ???
        // Can I cast somehow the object types that I push_back on my Collection ???
        // Note that this loop does not know the add order AACB that we pushed the Child pointers.

        identify(x);
    }
    /*
    The desired output of this loop should be:

    A
    A
    C
    B

    */

    return 0;
}

该代码也可以在Ideone使用

访客模式解决了这个问题。

基本上,添加一个虚拟方法Base::accept(Visitor& v)

每个孩子都将覆盖调用v.visit(*this)此类方法。

您的访问者类应如下所示:

class Visitor
{
public:
  void visit(A&) { /* this is A */ }
  void visit(B&) { /* this is B */ }
  void visit(C&) { /* this is C */ }
}

实例化您的访客: Visitor v;

循环调用x->accept(v);

http://ideone.com/2oT5S2

您可以在此处采用三种方法:OO和动态调度,访客和变体。 哪一种更好,将取决于您拥有的类型操作的数量,以及您更有可能添加的操作。

  1. 实际使用OO。 如果需要每个派生对象以不同的方式执行某些操作,则在OO中执行此操作的方式是添加一个虚拟成员函数:

     struct Base { virtual const char* name() = 0; }; struct A : Base { const char* name() override { return "A"; } // ... for (auto const& x : collection.getItems()) { std::cout << x->name() << std::endl; } 
  2. 使用访客模式。 这介于OO和功能之间-我们创建了一个基础对象,该对象知道如何与所有类型进行交互:

     struct Visitor; struct Base { virtual void visit(Visitor& ) = 0; }; struct A; struct B; struct C; struct Visitor { virtual void visit(A& ) = 0; virtual void visit(B& ) = 0; virtual void visit(C& ) = 0; }; struct A : Base { void visit(Visitor& v) override { v.visit(*this); } }; // ... struct IdentityVisitor : Visitor { void visit(A& ) { std::cout << "A" << std::endl; } void visit(B& ) { std::cout << "B" << std::endl; } void visit(C& ) { std::cout << "C" << std::endl; } }; IdentityVisitor iv; for (auto const& x : collection.getItems()) { x->visit(iv); } 
  3. 只需使用一个变体。 而不是存储shared_ptr<Base>的集合,而是存储variant<A,B,C>的集合,这些类型甚至不在层次结构中。 它们只是三个任意类型。 接着:

     for (auto const& x : collection.getItems()) { visit(overload( [](A const& ){ std::cout << "A" << std::endl; }, [](B const& ){ std::cout << "B" << std::endl; }, [](C const& ){ std::cout << "C" << std::endl; } ), x); } 

@Barry和@csguth感谢您的回答。

Barry的第三个选项非常有趣,我想尝试一下。 可以在此页面上找到带有boost :: static_visitorboost :: variant的工作示例。

就我而言,我没有考虑OO和虚拟方法,因为我想避免将逻辑放入这些A,B,C对象中。 关于访客模式,这是我想到的唯一好的选择。 但是,我希望发现一些更灵活的解决方案,例如“ LambdaVisitor”,并感谢您打开我的眼睛!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM