让我们假设我有一个超级多态基类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不实用,则检查返回值。
答案 0 :(得分:1)
您可以在typeid
标题中使用typeinfo
。
例如,请参阅此问题:How to determine actual object type at runtime in C++;
然而,实际的问题是&#34;为什么你需要知道对象的实际类型?&#34;:这是AFAIK并不经常需要这样的功能,因为多态性已经允许管理绝大多数用例。
我知道可以访问vector中每个形状的方法和成员 p,我需要使用dynamic_cast。
不,不一定!
在您的情况下,假设Shape
具有area
方法,Circle
和Rectangle
中定义了(重新),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;
}