C++中判断继承关系的几种方法

C++中判断继承关系的几种方法

  • 介绍
    平时在写C++代码时我们往往会有判断继承关系的需求,即判断一种类型是否派生于另外一种类型,或者一个对象是否是一个类型派生而来的类实例,可有两种思路可以实现。

  • 编译时判断
    你用模版元编程思想,使任务(其实是静态语义的推导)在编译期完成,没有任何运行时负担。
    首先看具体实现代码:

 

看起来很混乱,但是如果了解模板元编程,很容易看出代码中的template,enum hack和sizeof,这是模板元编程的惯用手法,下面就来解释一下这段代码的工作原理。

判断是否继承的重点在于Result,12行两个sizeof的比较实际上就是比较两个sizeof中表达式类型的大小,左边为int,而右边则为函数t的返回值的类型。而再看看上面两个利用了重载(overload)特性的函数t,似乎就有些眉目了。

t((T*)NULL)意味着传递的参数是指向T的指针,当T继承于TBase(或者就是TBase)的时候, C++会自动匹配上返回类型为int的函数t ,此时两边sizeof的表达式就都为int,Result的值为1。若不满足,则Result = sizeof(int)==sizeof(char),即为0.

这种方法的优点是如果只需要编译时判断时它不会带来运行时的负担,也不需要修改原有代码,但当我们需要在运行时进行判断时,这种方法就无用武之地了。

  • 运行时判断
    其实更多时候我们需要在运行时判定一个对象是否是由某个基类派生而来。

C++为了解决这个常见的需求,引入了RTTI来解决,在此之前,MFC曾通过一些奇巧淫技的宏来在源代码里维护一个继承链。而即使是RTTI被大多编译器支持后,仍然不被推荐使用。Google C++ Style Guide中提到,除测试外,不应该使用RTTI,同样不推荐手工构造类似RTTI的方案,如果需要由对象的类型决定代码的行为,应该用虚函数或访问者模式取代。

但是这并不妨碍我们掌握如何使用RTTI,至少在测试的时候我们仍然需要用到。

解决方案 :dyncamic_cast

dynamic_cast是判断继承关系最简单的方案,当我们使用dynamic_cast<T*>(x)试图将x转换为指向T的指针时,程序会判断x指向的对象是否是由T派生而来,如果不是,则返回NULL。

dynamic_cast使用起来虽然方便,但是它的性能却表现不佳,具体可以参见Stackoverflow上关于dynamic_cast性能的讨论:How expensive is RTTI?

  • 总结
    类型判断虽然不被推荐平时使用,但是在写测试时能够有效的验证我们的想法(因为测试不应该修改源代码),平时当我们想要通过对象类型行为来决定代码行为时,务必考虑清楚我们是否真的要这样做,是不是设计上有什么缺陷,换成虚函数是不是更好。

One thought on “C++中判断继承关系的几种方法

发表评论

电子邮件地址不会被公开。 必填项已用*标注

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax