如何正确检查一个对象是否实现了一个接口

7
我希望为某些对象实现自定义行为。
为此,我的项目(继承自QGraphicsItem)需要实现某些接口。
class SomeParentItem 
{ 
    SomeParentItem(bool x) { x = true; } 
    void function1() {}
};
class SomeInterface
{
    virtual void function2() = 0;
};
class XYZItem : public QGraphicsXYZItem, public SomeParentItem, public SomeInterface
{
    XYZItem(bool x):SomeParentItem(x) {}
    virtual void function2() { x = false; }
};
class MPQItem : public QGraphicsMPQItem, public SomeParentItem
{
    MPQItem (bool x):SomeParentItem(x) {}
};

从外部看,我认为我只需要做
SomeInterface* item1 = dynamic_cast<SomeInterface*>(item);
if(item1 == NULL)
    item->function1(); 
else
    item1->function2();  

不幸的是,这往往会导致崩溃...因此我正在创建一个标志来测试,只有当标志为真时才敢强制转换。

但我一直在想,它不应该崩溃。所以我变得勇敢,再次尝试,这次在QWidget子类中进行。与其崩溃,我得到了

QWidget::insertAction: Attempt to insert null action

出现该消息是因为测试条件 if(item1 == NULL) 不成立...

我应该如何正确检查我的对象是否实现了 SomeInterface 接口?

注意:对象 item 不能为空。


请注意,如果“item”为空,则您将在“item1”上调用“function2()”,而“item1”也将为空。 - lcs
“item” 不能为空。 - Thalia
2
你遇到了什么样的崩溃?你尝试过对item进行空值测试吗?这似乎是它崩溃的唯一方式。dynamic_cast是测试对象是否实现接口的常规方法。 - Serge Rogatch
1
在修复了小部件本身的一些问题之后,我不再收到错误信息了。我无法解释为什么之前消除测试也没有出现错误...感谢您查看我的问题,但似乎我根据症状正在解决错误的问题不正确。 - Thalia
3个回答

1

可能有一个编译器开关可以禁用运行时类型信息,这样就可以解释其行为。否则,如果动态转换起作用,则item1应该只是非空的。


0
一个简单的通用模板可以使代码更易读和更简单实现。
template < class T > 
        bool is_SomeParentItem( T& P)                                                                                                                                                                      
        {                                                                                                                                                                                             
          if ( dynamic_cast<SomeParentItem*>(&P) ) 
              return true;                                                                                                                                             
          else 
              return false;                                                                                                                                                                               
        }

template < class T > 
        bool is_SomeInterface( T& I)                                                                                                                                                                      
        {                                                                                                                                                                                             
          if ( dynamic_cast<SomeInterface*>(&I) ) 
              return true;                                                                                                                                             
          else 
              return false;                                                                                                                                                                               
        }

template < class T >
    void function( T& X)
      {
         if ( isSomeInterface(X) )
         { 
             dynamic_cast<SomeInterface*>(&X)->function2();
             return;
         }

         if ( isSomeParentItem(X) )
             dynamic_cast<SomeParentItem*>(&X)->function1();
      } 

你需要使用第二个函数模板来避免尝试在未实现它的类上调用function2。 如果你尝试这样做:
 if ( isSomeInterface(instance) ) instance.function2(); 

如果一个类没有实现 function2 函数,编译器将会因为无法绑定该实例的 function2 而失败,这是正确的但不直观,因为它看起来不会从左到右的逻辑中被调用。然而,在编译时编译器无法知道 isSomeInterface 将返回什么。

这种技术并不被许多人认为是一种“好的风格”编程方式,因为它可能导致 C++ 代码混乱和臃肿,并变得难以维护和调试。通常使用 dynamic_cast 来区分类层次结构中同名函数,特别是在基类与子类方法之间,以及在有多个继承且方法签名重叠的情况下,以确定选择正确的方法。

    // safe implements                                                                                                                                                                            
#define implements( C, I ) (    __extension__ ({                   \                                                                                                                          
        static bool impl=(dynamic_cast<I*>(&C))? true : false;     \                                                                                                                          
        impl;                                                      \                                                                                                                          
      })) 

     if ( implements( instance, SomeInterface ) ) ...

是一个宏,在运行时检查接口的兼容性,如果需要的话。 然而,要节制使用。 如果您经常这样做,那么您的代码很可能需要重构。


-2

像这样简单的东西怎么样:

class SomeInterface
{
    virtual void function2() = 0;
    virtual qint32 role() =0;
};

返回一个不同的整数以实现自定义行为?


1
这并没有回答问题,而且引入了神奇数字的依赖。 - underscore_d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接