摇曳的蔷薇
没有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=1a2aPtr->instanceOf<B>(): expected=0, value=0a2aPtr->instanceOf<C>(): expected=0, value=0a2aPtr->instanceOf<D>(): expected=0, value=0a2bPtr->instanceOf<A>(): expected=1, value=1a2bPtr->instanceOf<B>(): expected=1, value=1a2bPtr->instanceOf<C>(): expected=0, value=0a2bPtr->instanceOf<D>(): expected=0, value=0a2cPtr->instanceOf<A>(): expected=1, value=1a2cPtr->instanceOf<B>(): expected=0, value=0a2cPtr->instanceOf<C>(): expected=1, value=1a2cPtr->instanceOf<D>(): expected=0, value=0c2dPtr->instanceOf<A>(): expected=1, value=1c2dPtr->instanceOf<B>(): expected=0, value=0c2dPtr->instanceOf<C>(): expected=1, value=1c2dPtr->instanceOf<D>(): expected=1, value=1a2dPtr->instanceOf<A>(): expected=1, value=1a2dPtr->instanceOf<B>(): expected=0, value=0a2dPtr->instanceOf<C>(): expected=1, value=1a2dPtr->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 usInstanceOf performance(A->C) : 642157 usInstanceOf performance(A->B) : 671399 usInstanceOf performance(A->A) : 626193 usDynamicCast performance(A->D) : 754937 usDynamicCast performance(A->C) : 706766 usDynamicCast performance(A->B) : 751353 usDynamicCast 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 usInstanceOf performance(A->C) : 5030 usInstanceOf performance(A->B) : 5250 usInstanceOf performance(A->A) : 3021 usDynamicCast performance(A->D) : 666903 usDynamicCast performance(A->C) : 698567 usDynamicCast performance(A->B) : 727368 usDynamicCast 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 usInstanceOf performance(A->C) : 333994 usInstanceOf performance(A->B) : 334596 usInstanceOf performance(A->A) : 300959 usDynamicCast performance(A->D) : 331942 usDynamicCast performance(A->C) : 303715 usDynamicCast performance(A->B) : 400262 usDynamicCast performance(A->A) : 324942 us输出(带编译器优化):InstanceOf performance(A->D) : 209501 usInstanceOf performance(A->C) : 208727 usInstanceOf performance(A->B) : 207815 usInstanceOf performance(A->A) : 197953 usDynamicCast performance(A->D) : 259417 usDynamicCast performance(A->C) : 256203 usDynamicCast performance(A->B) : 261202 usDynamicCast performance(A->A) : 193535 us