[英]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;
}
访客模式解决了这个问题。
基本上,添加一个虚拟方法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);
。
您可以在此处采用三种方法:OO和动态调度,访客和变体。 哪一种更好,将取决于您拥有的类型和操作的数量,以及您更有可能添加的操作。
实际使用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; }
使用访客模式。 这介于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); }
只需使用一个变体。 而不是存储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_visitor和boost :: variant的工作示例。
就我而言,我没有考虑OO和虚拟方法,因为我想避免将逻辑放入这些A,B,C对象中。 关于访客模式,这是我想到的唯一好的选择。 但是,我希望发现一些更灵活的解决方案,例如“ LambdaVisitor”,并感谢您打开我的眼睛!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.