我看到了一些关于避免RTTI的问题,但我似乎更具体一些。以下是一个示例案例:
struct Base {};
struct A : Base {};
struct B : Base {};
struct C : Base {};
std::vector<Base*> vec;
我想对向量中所有可能的(无序的)对象做一些事情(如果向量有3个元素,0和1,0和2,1和2)。我想要的伪代码就是:
if e1 is A and e2 is A:
behavior1(e1, e2)
elif e1 is A and e2 is B:
behavior2(e1, e2)
elif ...
很多人都说RTTI设计不好,但可以在这里避免吗?如果/ elif,有没有比做所有这些更有效的方法?
答案 0 :(得分:2)
您是否选择使用或避免使用RTTI更多是关于常识。虽然可能被认为是一个很好的设计,努力避免它,偶尔你只是想完成一些事情并继续前进。
如果你只有几个类类型要处理,你可以摆脱if/else if
并有一个简单的函数指针表。在每种类型中使用虚函数来增加用于查找正确函数的索引的权重:
struct Base
{
virtual int GetWeight() const = 0;
};
struct A : Base
{
virtual int GetWeight() const { return 1; }
};
struct B : Base
{
virtual int GetWeight() const { return 2; }
};
struct C : Base
{
virtual int GetWeight() const { return 4; }
};
static void (*MyBehaviours)( Base*, Base* )[] = { &behaviour1, &behaviour2, ... };
MyBehaviours[ e1->GetWeight() + e2->GetWeight() ]( e1, e2 );
答案 1 :(得分:2)
这是二元变体访问的主要用例。
这会将运行时多态性稍微转移到静态多态(虽然类型判别式仍在boost::variant
中使用,但 不使用或不需要RTTI )。
另请注意,您如何不必为所有组合添加单独的案例:我已经演示了如何使用模板行为实现
我还展示了如何通过显示需要Base, A
的重载(接受A,B,C,...与...结合)来混合“经典”运行时多态类型A(或派生)的第二个参数。
最后,请注意,这种方法可以让你在rvalue-ness,const-ness,volatility上重载。
#include <iostream>
#include <boost/variant.hpp>
#include <string>
struct Base {};
struct A: Base {};
struct B: Base {};
struct C: Base {};
typedef boost::variant<A, B, C> MyType;
struct MyBehaviour : boost::static_visitor<std::string>
{
template <typename T>
std::string operator()(T const&, T const&) const { return "identical"; }
std::string operator()(A const&, B const&) const { return "A, B"; }
std::string operator()(A const&, C const&) const { return "A, C"; }
std::string operator()(Base const&, A const&) const { return "[ABC], A"; }
std::string operator()(Base const&, Base const&) const { return "Other"; }
};
int main()
{
MyBehaviour f;
MyType a = A{},
b = B{},
c = C{};
std::cout << boost::apply_visitor(f, a, b) << "\n";
std::cout << boost::apply_visitor(f, a, c) << "\n";
std::cout << boost::apply_visitor(f, a, a) << "\n";
std::cout << boost::apply_visitor(f, b, b) << "\n";
std::cout << boost::apply_visitor(f, c, c) << "\n";
std::cout << boost::apply_visitor(f, c, a) << "\n";
std::cout << boost::apply_visitor(f, c, b) << "\n";
}
查看 Live on Coliru ,输出:
A, B
A, C
identical
identical
identical
[ABC], A
Other
答案 2 :(得分:0)
我认为您可以使用地图缓存:
map<string, func*>;
func*
是功能指针,可指向函数behavior(A,B)
或behavior(A,C)
或behavior(B, C)
当您创建A和B insert(make_pair(AB, behavior(A,B))
的对象以及B and C
的对象时,当您想要使用A和B对象时,您可以从地图中获取,而对于B和C则相同
答案 3 :(得分:0)
将以下内容添加到基地:
virtual void behaviour(Context& context) = 0;
所有派生类的工具。
如果您可以对所有行为调用使context
相同,则应该能够消除对RTTI检查的任何需要。每个实现都可以使用context
所需的任何内容。
答案 4 :(得分:0)
使behavior()调用A,B,C对象中的虚函数来执行特定的工作。
struct Base
{
virtual doSomething(){};
};
struct A : Base
{
virtual doSomething(){ };
};
struct B : Base
{
virtual doSomething(){};
};
std::vector<Base*> vec;
void performOperation(Base* a, Base* b)
{
a->doSomething(a, b);
b->doSomething(a, b);
}
int myFunction
{
// ... code to select a pair of objects omitted
performOperation(a, b);
}