【问题标题】:how to find type of an object contained in a vector of polymorphic base class?如何查找包含在多态基类向量中的对象的类型?
【发布时间】:2017-09-16 12:44:37
【问题描述】:

假设我有一个超多态基类 Shape,其中许多其他形状类都派生自它。

现在,如果我有一个形状指针向量,其中包含一个指向不同形状类型列表的指针,如下所示:

vector<Shape*> p;  // p contains pointer to many different shape objects

我知道要访问向量 p 中每个形状的方法和成员,我需要使用 dynamic_cast。

但是如果我不知道向量 p 在运行时实际包含什么怎么办?如何在运行时安全地找到向量 p 中包含的对象的类型?

我也知道我可以检查通过 dynamic_cast 进行的转换是否返回 NULL 是否成功。但这是否意味着要在向量 p 中找到我的形状对象的实际类型,我必须这样做:

if (dynamic_cast<Circle*> p[i] !=NULL){

// do stuff

}

else if (...) {


}

并为所有其他形状类型重复此模式?

但是如果我有 100 个可能的形状,这会变得很麻烦。有没有更好的方法来实现这一点?

ps- 考虑以下场景:

假设我需要遍历 Shape* 向量,例如将所有圆形对象放在单独的向量和向量等中……现在我需要知道对象的实际类型。检查返回如果许多形状的 typeid 和 dynamic_casts 是不切实际的。

【问题讨论】:

  • 但我必须再次针对不同的形状类型检查 typeid。这并不能真正解决问题。
  • dynamic_cast 通常是一种反模式。改用虚方法。如果您需要根据另一个对象的类型动态调度代码,请给该对象一个虚拟的visit() 方法并改用visitor pattern

标签: c++ oop polymorphism runtime


【解决方案1】:

您可以在typeinfo 标头中使用typeid

例如看这个问题:How to determine actual object type at runtime in C++;

然而,实际的问题是“为什么你需要知道你的对象的实际类型?”:这是 AFAIK 并不经常需要这样的功能,因为多态性已经允许管理绝大多数用例.

我知道可以访问向量中每个形状的方法和成员 p,我需要使用dynamic_cast。

不,不一定! 在您的情况下,假设Shape 有一个area 方法,(重新)定义在CircleRectangle (它们都扩展了Shape 类)中,也许以下就足够了:

std::vector<Shape*> shapes;
Rectangle rect(...);
Circle circle(...);
shapes.push_back( &rect );
shapes.push_back( &circle );
shapes[0]->area(); // --> calls Rectangle::area()
shapes[1]->area(); // --> calls Circle::area()

【讨论】:

  • 谢谢。但考虑一下这种情况。让坐在我需要遍历 Shape* 向量,例如将所有圆形对象放在单独的向量 和 vector 等...现在我需要知道对象的实际类型。如果许多形状的 typeid 和 dynamic_casts 不可行,则检查返回。
【解决方案2】:

我想出了一个我并不真正引以为豪的解决方案,但也许它有助于创造更好的解决方案。 我试图实现的关键是摆脱显式的 dynamic_cast 并使这个工作正常。尽管如此,仍然需要将派生类型命名两次。 此外,它使用std::function,它被告知速度很慢。需要 C++14。 我相信有一种方法可以通过巧妙地使用模板来做到这一点。或者至少摆脱type_switch&lt;A&gt;::cast&lt;B&gt; 通道。无论如何,代码:

#include <iostream>
#include <functional>
#include <typeindex>
#include <unordered_map>

// Basic inheritance cases
struct A
{
    virtual void foo() = 0;
};

struct B : public A
{
    void foo() override { }
    void bfoo() {
        std::cout << "B specific\n";
    }
};

struct C : public A
{
    void foo() override  { }
};

template <typename T>
struct type_switch
{
    using Func = std::function<void(T&)>;
    using Pair = std::pair<std::type_index, Func>;
    using Map = std::unordered_map<std::type_index, Func>;
    Map map;

    type_switch(std::initializer_list<Pair> l) : map(l.begin(),l.end())
    {

    }

    void call(T& a)
    {
        map[typeid(a)](a);
    }

    // allows for "oneliner", without explicit 'call', but it could end in creation of 
    // new type_switch on every loop iteration etc.
    type_switch(T&a, std::initializer_list<Pair> l) : type_switch(l){
        call(a);
    }

    template <typename T2>
    static Func cast(std::function<void(T2&)> f)
    {
        static_assert(std::is_base_of<T, T2>::value, "Invalid cast operation on functors, T2 is not base of T");

        // lot of functor copyings...
        return[f = std::move(f)](T& t) {
            f(static_cast<T2&>(t));
        };
    }
};


int main()
{   
    B b;
    C c;

    int local = 0;

    type_switch<A> sw = { 
            { typeid(B), type_switch<A>::cast<B>( [&local](auto& a) { // auto will deduce into B! No explicit casting
            std::cout << "Handle b, local value is " << local << '\n';
            a.bfoo(); // B specific
            local++; // some outer scode operation
        }) } ,
        { typeid(C), type_switch<A>::cast<C>([&local](auto& a) {  // auto will deduce into C! No explicit casting
            std::cout << "Handle c, local value is " << local << '\n';
            local++; // some outer scode operation
        })
        },
        /*  // this one would trigger static_assert
         { typeid(int), type_switch<A>::cast<int>([&local](auto& a) {  // auto will deduce into C! No explicit casting
            std::cout << "Handle int, local value is " << local << '\n';
            local++; // some outer scode operation
        })
        },*/
    };
    sw.call(b);
    sw.call(c);
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-04
    • 1970-01-01
    • 2021-09-05
    • 2012-12-02
    • 1970-01-01
    • 2013-12-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多