【问题标题】:Testing for Type Equality without RTTI没有 RTTI 的类型相等性测试
【发布时间】:2011-01-15 14:29:25
【问题描述】:

BC 派生自A。我希望能够测试从A 派生的任何两个类实例是否是同一类的实例,即A* fooA* bar 是否都指向B 实例,而不使用RTTI。我目前的解决方案是这样的:

class A {
protected:

    typedef uintptr_t Code;
    virtual Code code() const = 0;

}; // class A


class B : public A {
protected:

    virtual Code code() const { return Code(&identity); }

private:

    static int identity;

}; // class B


class C : public A {
protected:

    virtual Code code() const { return Code(&identity); }

private:

    static int identity;

}; // class C

使用这种方法,operator== 可以简单地测试first.code() == second.code()。我想从派生类中删除文字identity 并让A 自动找到代码,这样并非所有派生类都必须重复这个习语。同样,我强烈不希望使用 RTTI。有没有办法做到这一点?

注意:我看过最近的问题[1][2],这不是重复的。那些张贴者想要测试其派生类的内容;我只想测试身份

【问题讨论】:

  • 如果你不使用 RTTI,你注定要重新发明它。不完美。
  • 出于好奇:您为什么更喜欢使用这种方法而不是使用 RTTI?
  • @nobugz:我不会重新发明整个事情;我只模拟了我会从更大的内置功能中使用的一个功能。 @Laurence:这个项目必须能够在任何地方运行。除了比较两个整数之外,我不希望空间开销和运行时开销。
  • RTTI 没有任何功能。它只做一件事,开销非常
  • @Jon:你真的测量你想要避免的“开销”吗?一旦有了虚拟功能,RTTI 就几乎没有开销。我能想到的唯一一个是每个类 type 一个类对象。除非您的编译器允许您关闭 RTTI(就像 VC 那样),否则无论如何您都会拥有它。

标签: c++ equality rtti


【解决方案1】:

您应该只使用 RTTI 而不是重新发明轮子。

如果您坚持不使用 RTTI,您可以使用 CRTP 和函数局部静态变量来避免将函数写入每个派生类。改编自我为维基百科编写的示例代码:http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern#Polymorphic_copy_construction

另一种选择是读取 vtable 指针(通过 this 和指针算法),但这取决于编译器和平台,因此它不可移植。

【讨论】:

  • 您使用 CRTP 的建议完美地消除了派生类中的重复。谢谢!现在我只是在想办法让它不占用空间。
  • 请注意,函数本身(程序代码)比其中的静态变量占用更多空间。
【解决方案2】:

你的想法是正确的;也许您可以使用模板消除一些样板:

class TypeTagged {
public:
  virtual Code code() const = 0;
}

template <class T>
class TypeTaggedImpl: public virtual TypeTagged {
public:
  virtual Code code() const { return Code(&id); }
private:
  static int id;
}

那么你的客户端类只需要像这样声明:

class A: public TypeTaggedImpl<A> { ... }

class B: public A, public TypeTaggedImpl<B> { ... }

TypeTagged 的不同实例化意味着这些类型具有不同的id 字段,因此具有不同的 ID;虚拟基类型意味着返回最多派生类型的code

【讨论】:

  • 这实际上与 Tronic 的答案相同,但 +1 用于提供示例。
【解决方案3】:

您可以让基类将id 作为构造函数参数,并在基类本身中实现identity() 函数。那么就不需要在派生类中重复代码了。在派生类构造函数中,你可以做类似derived::derived(): base(0) 示例代码:

class A
{
public:
    A(int n) : m_id(n)
    {
    }
    virtual ~A(){}

    virtual int id() const 
    {
        return m_id;
    }

private:
    int m_id;
};

class B : public A
{
public:
    B() : A(0)
    {
    }
};

【讨论】:

  • 好主意,但它仍然需要所有派生类做额外的工作,并引入了冲突的可能性。此外,理想情况下,身份应该不需要存储。
  • 这很糟糕,因为它用类型标签标记每个实例,而实际上只需要将标签与类相关联。
【解决方案4】:

您可以使用两个宏 __FILE__ __LINE__ 作为您的代码
这样可以避免碰撞问题
您可以将此值映射到 int

【讨论】:

  • 隐式模板实例化将共享相同的__FILE____LINE__ 但属于不同的类,所以这是不完美的。不过有趣的黑客攻击。
  • 它必须是 __FILE__ + __LINE__ 或类似的,并且它必须为每个派生类出现一次,这正是我想要避免的。它还将使用存储空间,而不仅仅是int
猜你喜欢
  • 2019-10-09
  • 1970-01-01
  • 1970-01-01
  • 2021-09-24
  • 2011-03-27
  • 2016-04-02
  • 1970-01-01
  • 1970-01-01
  • 2013-03-13
相关资源
最近更新 更多