如何查找多态基类向量中包含的对象的类型?

时间:2017-09-16 12:44:37

标签: c++ oop polymorphism runtime

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

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

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

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

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

我也知道我可以通过dynamic_cast检查是否为了成功而返回NULL。但这是否意味着在矢量p中找到我的形状对象的实际类型我必须做这样的事情:

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

// do stuff

}

else if (...) {


}

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

但如果我有100种可能的形状,这会变得很麻烦。有什么更好的方法可以在朗姆酒时间实现这一目标吗?

ps-考虑以下情况:

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

2 个答案:

答案 0 :(得分:1)

您可以在typeid标题中使用typeinfo

例如,请参阅此问题:How to determine actual object type at runtime in C++;

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

  

我知道可以访问vector中每个形状的方法和成员   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() ,则可能以下内容就足够了} class):

v-html

答案 1 :(得分:0)

我想出了一个我并不为之骄傲的解决方案,但也许它可以帮助我创造更好的解决方案。 我试图实现的关键是摆脱显式的dynamic_cast并让这个工作。仍然需要两次命名你的derieved类型。 此外,它使用std::function,它被告知速度很慢。需要C ++ 14。 我相信只需聪明地使用模板就可以做到这一点。或者至少摆脱type_switch<A>::cast<B>车道。无论如何,代码:

#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;
}