为了账号安全,请及时绑定邮箱和手机立即绑定

C ++相当于instanceof

C ++相当于instanceof

C++
红颜莎娜 2019-08-12 17:07:22
C ++相当于instanceof实现C ++等价的首选方法是instanceof什么?
查看完整描述

3 回答

?
Cats萌萌

TA贡献1805条经验 获得超9个赞

尝试使用:


if(NewType* v = dynamic_cast<NewType*>(old)) {

   // old was safely casted to NewType

   v->doSomething();

}

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


编辑:我对这个答案有一些好评!


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


典型的解决方法是将要检查的类的特殊行为放入基类的虚函数中,或者引入类似访问者的内容,您可以在不改变界面的情况下为子类引入特定行为(除了添加访问者接受接口)课程)。


正如所指出的,dynamic_cast不是免费的。处理大多数(但不是所有情况)的简单且始终如一的执行hack基本上是添加一个枚举,表示您的类可以拥有的所有可能类型,并检查您是否拥有正确的类型。


if(old->getType() == BOX) {

   Box* box = static_cast<Box*>(old);

   // Do something box specific

}

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


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


// 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

}


查看完整回答
反对 回复 2019-08-12
?
摇曳的蔷薇

TA贡献1793条经验 获得超6个赞


没有dynamic_cast的实现实例

我认为这个问题今天仍然有用。使用C ++ 11标准,您现在可以实现一个instanceof函数而不使用dynamic_cast这样的:


if (dynamic_cast<B*>(aPtr) != nullptr) {

  // aPtr is instance of B

} else {

  // aPtr is NOT instance of B

}

但你依然依赖RTTI支持。所以这是我的解决方案,取决于一些宏和Metaprogramming魔术。唯一的缺点是恕我直言,这种做法并没有对工作的多重继承。


InstanceOfMacros.h


#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 *


#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


#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<B>(): expected=0, value=" << a2aPtr->instanceOf<B>() << 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<B>(): expected=1, value=" << a2bPtr->instanceOf<B>() << 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<B>(): expected=0, value=" << a2cPtr->instanceOf<B>() << 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<B>(): expected=0, value=" << c2dPtr->instanceOf<B>() << 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<B>(): expected=0, value=" << a2dPtr->instanceOf<B>() << 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;

}

输出:


a2aPtr->instanceOf<A>(): expected=1, value=1

a2aPtr->instanceOf<B>(): 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<B>(): 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<B>(): 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<B>(): 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<B>(): expected=0, value=0

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

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

性能

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


InstanceOfPerformance.cpp


#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

            << "\n"

            << 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

            << "\n"

            << std::endl;

  return 0;

}

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


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。在我的本地机器上的结果是惊人的:


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和模板元编程的对手,并且不太懒于在类层次结构的类中添加一些小指令,那么这种方法可以提升你的应用程序一点点关于它的性能,如果你经常最终检查指针的实例。但要谨慎使用它。这种方法的正确性无法保证。


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


编辑: 我还测试了Linux机器上的性能gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609。在这个平台上,性能优势并不像具有铿锵声的macOs那么重要。


输出(无编译器优化):


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

输出(带编译器优化):


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


查看完整回答
反对 回复 2019-08-12
  • 3 回答
  • 0 关注
  • 925 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信