关于oop:相当于instanceof的C ++

C++ equivalent of instanceof

实现与instanceof等效的C ++的首选方法是什么?


尝试使用:

1
2
3
4
if(NewType* v = dynamic_cast<NewType*>(old)) {
   // old was safely casted to NewType
   v->doSomething();
}

这要求您的编译器启用rtti支持。

编辑:
我对这个答案有很好的评论!

每次需要使用dynamic_cast(或instanceof)时,最好问问自己是否必要。通常这是不良设计的标志。

典型的变通办法是将要检查的类的特殊行为放入基类的虚函数中,或者引入诸如访问者之类的东西,您可以在不更改接口的情况下为子类引入特定行为(除了添加的访问者接受接口)课程)。

如前所述,dynamic_cast不是免费提供的。一个可以处理大多数(但不是所有情况)的简单且性能一致的hack基本上是添加一个枚举,表示您的类可以拥有的所有可能的类型,并检查您是否找到了正确的类型。

1
2
3
4
if(old->getType() == BOX) {
   Box* box = static_cast<Box*>(old);
   // Do something box specific
}

这不是一个好的设计,但它可能是一种解决方法,其成本或多或少只是一个虚拟函数调用。无论是否启用了RTTI,它也可以工作。

请注意,这种方法不支持多级继承,因此,如果您不小心,可能会以如下代码结尾:

1
2
3
4
5
6
// Here we have a SpecialBox class that inherits Box, since it has its own type
// we must check for both BOX or SPECIAL_BOX
if(old->getType() == BOX || old->getType() == SPECIAL_BOX) {
   Box* box = static_cast<Box*>(old);
   // Do something box specific
}


根据您要执行的操作,可以执行以下操作:

1
2
3
4
template<typename Base, typename T>
inline bool instanceof(const T*) {
    return std::is_base_of<Base, T>::value;
}

采用:

1
if (instanceof<BaseClass>(ptr)) { ... }

但是,这仅对编译器已知的类型起作用。

编辑:

此代码应适用于多态指针:

1
2
3
4
template<typename Base, typename T>
inline bool instanceof(const T *ptr) {
    return dynamic_cast<const Base*>(ptr) != nullptr;
}

示例:http://cpp.sh/6qir


没有dynamic_cast的实现实例

我认为这个问题今天仍然有意义。使用C ++ 11标准,您现在可以实现instanceof函数,而无需像这样使用dynamic_cast

1
2
3
4
5
if (dynamic_cast<B*>(aPtr) != nullptr) {
  // aPtr is instance of B
} else {
  // aPtr is NOT instance of B
}

但是您仍然依赖RTTI支持。因此,这是我针对此问题的解决方案,具体取决于一些宏和元编程魔术。恕我直言,唯一的缺点是这种方法不适用于多重继承。

InstanceOfMacros.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <set>
#include <tuple>
#include <typeindex>

#define _EMPTY_BASE_TYPE_DECL() using BaseTypes = std::tuple<>;
#define _BASE_TYPE_DECL(Class, BaseClass) \
  using BaseTypes = decltype(std::tuple_cat(std::tuple<BaseClass>(), Class::BaseTypes()));

#define _INSTANCE_OF_DECL_BODY(Class)                                 \
  static const std::set<std::type_index> baseTypeContainer;           \
  virtual bool instanceOfHelper(const std::type_index &_tidx) {       \
    if (std::type_index(typeid(ThisType)) == _tidx) return true;      \
    if (std::tuple_size<BaseTypes>::value == 0) return false;         \
    return baseTypeContainer.find(_tidx) != baseTypeContainer.end();  \
  }                                                                   \
  template <typename... T>                                            \
  static std::set<std::type_index> getTypeIndexes(std::tuple<T...>) { \
    return std::set<std::type_index>{std::type_index(typeid(T))...};  \
  }


#define INSTANCE_OF_SUB_DECL(Class, BaseClass) \
 protected:                                    \
  using ThisType = Class;                      \
  _BASE_TYPE_DECL(Class, BaseClass)            \
  _INSTANCE_OF_DECL_BODY(Class)


#define INSTANCE_OF_BASE_DECL(Class)                                                    \
 protected:                                                                             \
  using ThisType = Class;                                                               \
  _EMPTY_BASE_TYPE_DECL()                                                               \
  _INSTANCE_OF_DECL_BODY(Class)                                                         \
 public:                                                                                \
  template <typename Of>                                                                \
  typename std::enable_if<std::is_base_of<Class, Of>::value, bool>::type instanceOf() { \
    return instanceOfHelper(std::type_index(typeid(Of)));                               \
  }


#define INSTANCE_OF_IMPL(Class) \
  const std::set<std::type_index> Class::baseTypeContainer = Class::getTypeIndexes(Class::BaseTypes());

演示版

然后,您可以按以下方式使用此内容(谨慎使用):

DemoClassHierarchy.hpp *

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include"InstanceOfMacros.h"

struct A {
  virtual ~A() {}
  INSTANCE_OF_BASE_DECL(A)
};
INSTANCE_OF_IMPL(A)

struct B : public A {
  virtual ~B() {}
  INSTANCE_OF_SUB_DECL(B, A)
};
INSTANCE_OF_IMPL(B)

struct C : public A {
  virtual ~C() {}
  INSTANCE_OF_SUB_DECL(C, A)
};
INSTANCE_OF_IMPL(C)

struct D : public C {
  virtual ~D() {}
  INSTANCE_OF_SUB_DECL(D, C)
};
INSTANCE_OF_IMPL(D)

以下代码演示了一个小示例,以验证基本的正确行为。

InstanceOfDemo.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <memory>
#include"DemoClassHierarchy.hpp"

int main() {
  A *a2aPtr = new A;
  A *a2bPtr = new B;
  std::shared_ptr<A> a2cPtr(new C);
  C *c2dPtr = new D;
  std::unique_ptr<A> a2dPtr(new D);

  std::cout <<"a2aPtr->instanceOf<A>(): expected=1, value=" << a2aPtr->instanceOf<A>() << std::endl;
  std::cout <<"a2aPtr->instanceOf(): expected=0, value=" << a2aPtr->instanceOf() << std::endl;
  std::cout <<"a2aPtr->instanceOf<C>(): expected=0, value=" << a2aPtr->instanceOf<C>() << std::endl;
  std::cout <<"a2aPtr->instanceOf<D>(): expected=0, value=" << a2aPtr->instanceOf<D>() << std::endl;
  std::cout << std::endl;
  std::cout <<"a2bPtr->instanceOf<A>(): expected=1, value=" << a2bPtr->instanceOf<A>() << std::endl;
  std::cout <<"a2bPtr->instanceOf(): expected=1, value=" << a2bPtr->instanceOf() << std::endl;
  std::cout <<"a2bPtr->instanceOf<C>(): expected=0, value=" << a2bPtr->instanceOf<C>() << std::endl;
  std::cout <<"a2bPtr->instanceOf<D>(): expected=0, value=" << a2bPtr->instanceOf<D>() << std::endl;
  std::cout << std::endl;
  std::cout <<"a2cPtr->instanceOf<A>(): expected=1, value=" << a2cPtr->instanceOf<A>() << std::endl;
  std::cout <<"a2cPtr->instanceOf(): expected=0, value=" << a2cPtr->instanceOf() << std::endl;
  std::cout <<"a2cPtr->instanceOf<C>(): expected=1, value=" << a2cPtr->instanceOf<C>() << std::endl;
  std::cout <<"a2cPtr->instanceOf<D>(): expected=0, value=" << a2cPtr->instanceOf<D>() << std::endl;
  std::cout << std::endl;
  std::cout <<"c2dPtr->instanceOf<A>(): expected=1, value=" << c2dPtr->instanceOf<A>() << std::endl;
  std::cout <<"c2dPtr->instanceOf(): expected=0, value=" << c2dPtr->instanceOf() << std::endl;
  std::cout <<"c2dPtr->instanceOf<C>(): expected=1, value=" << c2dPtr->instanceOf<C>() << std::endl;
  std::cout <<"c2dPtr->instanceOf<D>(): expected=1, value=" << c2dPtr->instanceOf<D>() << std::endl;
  std::cout << std::endl;
  std::cout <<"a2dPtr->instanceOf<A>(): expected=1, value=" << a2dPtr->instanceOf<A>() << std::endl;
  std::cout <<"a2dPtr->instanceOf(): expected=0, value=" << a2dPtr->instanceOf() << std::endl;
  std::cout <<"a2dPtr->instanceOf<C>(): expected=1, value=" << a2dPtr->instanceOf<C>() << std::endl;
  std::cout <<"a2dPtr->instanceOf<D>(): expected=1, value=" << a2dPtr->instanceOf<D>() << std::endl;

  delete a2aPtr;
  delete a2bPtr;
  delete c2dPtr;

  return 0;
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
a2aPtr->instanceOf<A>(): expected=1, value=1
a2aPtr->instanceOf(): expected=0, value=0
a2aPtr->instanceOf<C>(): expected=0, value=0
a2aPtr->instanceOf<D>(): expected=0, value=0

a2bPtr->instanceOf<A>(): expected=1, value=1
a2bPtr->instanceOf(): expected=1, value=1
a2bPtr->instanceOf<C>(): expected=0, value=0
a2bPtr->instanceOf<D>(): expected=0, value=0

a2cPtr->instanceOf<A>(): expected=1, value=1
a2cPtr->instanceOf(): expected=0, value=0
a2cPtr->instanceOf<C>(): expected=1, value=1
a2cPtr->instanceOf<D>(): expected=0, value=0

c2dPtr->instanceOf<A>(): expected=1, value=1
c2dPtr->instanceOf(): expected=0, value=0
c2dPtr->instanceOf<C>(): expected=1, value=1
c2dPtr->instanceOf<D>(): expected=1, value=1

a2dPtr->instanceOf<A>(): expected=1, value=1
a2dPtr->instanceOf(): expected=0, value=0
a2dPtr->instanceOf<C>(): expected=1, value=1
a2dPtr->instanceOf<D>(): expected=1, value=1

性能

现在出现的最有趣的问题是,这种邪恶的东西是否比dynamic_cast的使用更有效率。因此,我编写了一个非常基本的性能评估应用程序。

InstanceOfPerformance.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <chrono>
#include <iostream>
#include <string>
#include"DemoClassHierarchy.hpp"

template <typename Base, typename Derived, typename Duration>
Duration instanceOfMeasurement(unsigned _loopCycles) {
  auto start = std::chrono::high_resolution_clock::now();
  volatile bool isInstanceOf = false;
  for (unsigned i = 0; i < _loopCycles; ++i) {
    Base *ptr = new Derived;
    isInstanceOf = ptr->template instanceOf<Derived>();
    delete ptr;
  }
  auto end = std::chrono::high_resolution_clock::now();
  return std::chrono::duration_cast<Duration>(end - start);
}

template <typename Base, typename Derived, typename Duration>
Duration dynamicCastMeasurement(unsigned _loopCycles) {
  auto start = std::chrono::high_resolution_clock::now();
  volatile bool isInstanceOf = false;
  for (unsigned i = 0; i < _loopCycles; ++i) {
    Base *ptr = new Derived;
    isInstanceOf = dynamic_cast<Derived *>(ptr) != nullptr;
    delete ptr;
  }
  auto end = std::chrono::high_resolution_clock::now();
  return std::chrono::duration_cast<Duration>(end - start);
}

int main() {
  unsigned testCycles = 10000000;
  std::string unit =" us";
  using DType = std::chrono::microseconds;

  std::cout <<"InstanceOf performance(A->D)  :" << instanceOfMeasurement<A, D, DType>(testCycles).count() << unit
            << std::endl;
  std::cout <<"InstanceOf performance(A->C)  :" << instanceOfMeasurement<A, C, DType>(testCycles).count() << unit
            << std::endl;
  std::cout <<"InstanceOf performance(A->B)  :" << instanceOfMeasurement<A, B, DType>(testCycles).count() << unit
            << std::endl;
  std::cout <<"InstanceOf performance(A->A)  :" << instanceOfMeasurement<A, A, DType>(testCycles).count() << unit
            <<"
"

            << std::endl;
  std::cout <<"DynamicCast performance(A->D) :" << dynamicCastMeasurement<A, D, DType>(testCycles).count() << unit
            << std::endl;
  std::cout <<"DynamicCast performance(A->C) :" << dynamicCastMeasurement<A, C, DType>(testCycles).count() << unit
            << std::endl;
  std::cout <<"DynamicCast performance(A->B) :" << dynamicCastMeasurement<A, B, DType>(testCycles).count() << unit
            << std::endl;
  std::cout <<"DynamicCast performance(A->A) :" << dynamicCastMeasurement<A, A, DType>(testCycles).count() << unit
            <<"
"

            << std::endl;
  return 0;
}

结果有所不同,并且基本上基于编译器的优化程度。使用g++ -std=c++11 -O0 -o instanceof-performance InstanceOfPerformance.cpp编译性能测量程序,本地计算机上的输出为:

1
2
3
4
5
6
7
8
9
InstanceOf performance(A->D)  : 699638 us
InstanceOf performance(A->C)  : 642157 us
InstanceOf performance(A->B)  : 671399 us
InstanceOf performance(A->A)  : 626193 us

DynamicCast performance(A->D) : 754937 us
DynamicCast performance(A->C) : 706766 us
DynamicCast performance(A->B) : 751353 us
DynamicCast performance(A->A) : 676853 us

嗯,这个结果非常令人震惊,因为时间表明,新方法与dynamic_cast方法相比并没有快得多。对于测试A的指针是否为A的实例的特殊测试用例,效率甚至更低。但是,通过使用编译器otpimization来调整二进制文件,可以扭转局面。相应的编译器命令为g++ -std=c++11 -O3 -o instanceof-performance InstanceOfPerformance.cpp。我的本地计算机上的结果是惊人的:

1
2
3
4
5
6
7
8
9
InstanceOf performance(A->D)  : 3035 us
InstanceOf performance(A->C)  : 5030 us
InstanceOf performance(A->B)  : 5250 us
InstanceOf performance(A->A)  : 3021 us

DynamicCast performance(A->D) : 666903 us
DynamicCast performance(A->C) : 698567 us
DynamicCast performance(A->B) : 727368 us
DynamicCast performance(A->A) : 3098 us

如果您不依赖于多重继承,不反对使用旧的C宏,RTTI和模板元编程,并且不懒于向类层次结构的类中添加一些小指令,那么这种方法可以使您的应用程序有所提升关于它的性能,如果您经常要检查指针的实例。但是请谨慎使用。不保证这种方法的正确性。

注意:所有演示都是使用MacBook Pro Mid 2012上的macOS Sierra下的clang (Apple LLVM version 9.0.0 (clang-900.0.39.2))编译的。

编辑:
我还使用gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609在Linux机器上测试了性能。在此平台上,性能优势不如带有c声的macO显着。

输出(没有编译器优化):

1
2
3
4
5
6
7
8
9
InstanceOf performance(A->D)  : 390768 us
InstanceOf performance(A->C)  : 333994 us
InstanceOf performance(A->B)  : 334596 us
InstanceOf performance(A->A)  : 300959 us

DynamicCast performance(A->D) : 331942 us
DynamicCast performance(A->C) : 303715 us
DynamicCast performance(A->B) : 400262 us
DynamicCast performance(A->A) : 324942 us

输出(使用编译器优化):

1
2
3
4
5
6
7
8
9
InstanceOf performance(A->D)  : 209501 us
InstanceOf performance(A->C)  : 208727 us
InstanceOf performance(A->B)  : 207815 us
InstanceOf performance(A->A)  : 197953 us

DynamicCast performance(A->D) : 259417 us
DynamicCast performance(A->C) : 256203 us
DynamicCast performance(A->B) : 261202 us
DynamicCast performance(A->A) : 193535 us


已知dynamic_cast是无效的。它遍历继承层次结构,并且是具有多个继承级别并且需要检查对象是否是其类型层次结构中任何一种类型的实例的唯一解决方案。

但是,如果instanceof的形式更为有限,它仅检查对象是否恰好是您指定的类型,足以满足您的需求,则下面的函数将效率更高:

1
2
3
4
template<typename T, typename K>
inline bool isType(const K &k) {
    return typeid(T).hash_code() == typeid(k).hash_code();
}

这是如何调用上面的函数的示例:

1
2
3
4
5
DerivedA k;
Base *p = &k;

cout << boolalpha << isType<DerivedA>(*p) << endl;  // true
cout << boolalpha << isType<DerivedB>(*p) << endl;  // false

您将指定模板类型A(作为要检查的类型),然后传入要测试的对象作为参数(将从中推断出模板类型K)。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream.h>
#include<typeinfo.h>

template<class T>
void fun(T a)
{
  if(typeid(T) == typeid(int))
  {
     //Do something
     cout<<"int";
  }
  else if(typeid(T) == typeid(float))
  {
     //Do Something else
     cout<<"float";
  }
}

void main()
 {
      fun(23);
      fun(90.67f);
 }


这对我来说非常适合使用Code :: Blocks IDE和GCC编译器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include<iostream>
#include<typeinfo>
#include<iomanip>
#define SIZE 20
using namespace std;

class Publication
{
protected:
    char title[SIZE];
    int price;

public:
    Publication()
    {
        cout<<endl<<" Enter title of media :";
        cin>>title;

        cout<<endl<<" Enter price of media :";
        cin>>price;
    }

    virtual void show()=0;
};

class Book : public Publication
{
    int pages;

public:
    Book()
    {
        cout<<endl<<" Enter number of pages :";
        cin>>pages;
    }

    void show()
    {
        cout<<endl<<setw(12)<<left<<" Book Title"<<":"<<title;
        cout<<endl<<setw(12)<<left<<" Price"<<":"<<price;
        cout<<endl<<setw(12)<<left<<" Pages"<<":"<<pages;
        cout<<endl<<" ----------------------------------------";
    }
};

class Tape : public Publication
{
    int duration;

public:
    Tape()
    {
        cout<<endl<<" Enter duration in minute :";
        cin>>duration;
    }

    void show()
    {
        cout<<endl<<setw(10)<<left<<" Tape Title"<<":"<<title;
        cout<<endl<<setw(10)<<left<<" Price"<<":"<<price;
        cout<<endl<<setw(10)<<left<<" Duration"<<":"<<duration<<" minutes";
        cout<<endl<<" ----------------------------------------";
    }
};
int main()
{
    int n, i, type;

    cout<<endl<<" Enter number of media :";
    cin>>n;

    Publication **p = new Publication*[n];
    cout<<endl<<" Enter"<<n<<" media details :";

    for(i=0;i<n;i++)
    {
        cout<<endl<<" Select Media Type [ 1 - Book / 2 - Tape ]";
        cin>>type;

        if ( type == 1 )
        {
            p[i] = new Book();
        }
        else
        if ( type == 2 )
        {
            p[i] = new Tape();
        }
        else
        {
            i--;
            cout<<endl<<" Invalid type. You have to Re-enter choice";
        }
    }

    for(i=0;i<n;i++)
    {
        if ( typeid(Book) == typeid(*p[i]) )
        {
            p[i]->show();
        }
    }

    return 0;
}