3 回答

TA貢獻(xiàn)1805條經(jīng)驗(yàn) 獲得超9個(gè)贊
嘗試使用:
if(NewType* v = dynamic_cast<NewType*>(old)) {
// old was safely casted to NewType
v->doSomething();
}
這要求您的編譯器啟用rtti支持。
編輯:我對(duì)這個(gè)答案有一些好評(píng)!
每次你需要使用dynamic_cast(或instanceof)時(shí),你最好問問自己這是否是必要的。這通常是設(shè)計(jì)不佳的標(biāo)志。
典型的解決方法是將要檢查的類的特殊行為放入基類的虛函數(shù)中,或者引入類似訪問者的內(nèi)容,您可以在不改變界面的情況下為子類引入特定行為(除了添加訪問者接受接口)課程)。
正如所指出的,dynamic_cast不是免費(fèi)的。處理大多數(shù)(但不是所有情況)的簡(jiǎn)單且始終如一的執(zhí)行hack基本上是添加一個(gè)枚舉,表示您的類可以擁有的所有可能類型,并檢查您是否擁有正確的類型。
if(old->getType() == BOX) {
Box* box = static_cast<Box*>(old);
// Do something box specific
}
這不是好設(shè)計(jì),但它可以是一種解決方法,其成本或多或少只是虛擬函數(shù)調(diào)用。無(wú)論是否啟用RTTI,它都可以工作。
請(qǐng)注意,此方法不支持多級(jí)繼承,因此如果您不小心,可能會(huì)以如下代碼結(jié)束:
// 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
}

TA貢獻(xiàn)1793條經(jīng)驗(yàn) 獲得超6個(gè)贊
沒有dynamic_cast的實(shí)現(xiàn)實(shí)例
我認(rèn)為這個(gè)問題今天仍然有用。使用C ++ 11標(biāo)準(zhǔn),您現(xiàn)在可以實(shí)現(xiàn)一個(gè)instanceof函數(shù)而不使用dynamic_cast這樣的:
if (dynamic_cast<B*>(aPtr) != nullptr) {
// aPtr is instance of B
} else {
// aPtr is NOT instance of B
}
但你依然依賴RTTI支持。所以這是我的解決方案,取決于一些宏和Metaprogramming魔術(shù)。唯一的缺點(diǎn)是恕我直言,這種做法并沒有對(duì)工作的多重繼承。
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());
演示
然后你可以使用這些東西(謹(jǐn)慎),如下所示:
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)
以下代碼提供了一個(gè)小型演示,用于驗(yàn)證基本的正確行為。
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
性能
現(xiàn)在出現(xiàn)的最有趣的問題是,如果這種邪惡的東西比使用它更有效dynamic_cast。因此,我寫了一個(gè)非?;镜男阅軠y(cè)量應(yīng)用程序。
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;
}
結(jié)果各不相同,主要基于編譯器優(yōu)化的程度。使用g++ -std=c++11 -O0 -o instanceof-performance InstanceOfPerformance.cpp本地計(jì)算機(jī)上的輸出編譯性能測(cè)量程序是:
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
嗯,這個(gè)結(jié)果非常清醒,因?yàn)闀r(shí)間表明新方法與dynamic_cast方法相比并不快。對(duì)于測(cè)試指針A是否是實(shí)例的特殊測(cè)試用例來(lái)說(shuō)效率更低A。但是,通過(guò)使用編譯器otpimization調(diào)整我們的二進(jìn)制來(lái)轉(zhuǎn)變趨勢(shì)。相應(yīng)的編譯器命令是g++ -std=c++11 -O3 -o instanceof-performance InstanceOfPerformance.cpp。在我的本地機(jī)器上的結(jié)果是驚人的:
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和模板元編程的對(duì)手,并且不太懶于在類層次結(jié)構(gòu)的類中添加一些小指令,那么這種方法可以提升你的應(yīng)用程序一點(diǎn)點(diǎn)關(guān)于它的性能,如果你經(jīng)常最終檢查指針的實(shí)例。但要謹(jǐn)慎使用它。這種方法的正確性無(wú)法保證。
注意:所有演示都是clang (Apple LLVM version 9.0.0 (clang-900.0.39.2))在MacBook Pro Mid 2012上使用macOS Sierra 編譯的。
編輯: 我還測(cè)試了Linux機(jī)器上的性能gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609。在這個(gè)平臺(tái)上,性能優(yōu)勢(shì)并不像具有鏗鏘聲的macOs那么重要。
輸出(無(wú)編譯器優(yōu)化):
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
輸出(帶編譯器優(yōu)化):
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
- 3 回答
- 0 關(guān)注
- 966 瀏覽
添加回答
舉報(bào)