处理对象对时避免使用RTTI

时间:2013-08-02 09:20:52

标签: c++ polymorphism rtti

我看到了一些关于避免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,有没有比做所有这些更有效的方法?

5 个答案:

答案 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);
}