เป็นวิธีที่ต้องการเพื่อให้ได้เทียบเท่า C ++ ของ java คือinstanceof
อะไร
เป็นวิธีที่ต้องการเพื่อให้ได้เทียบเท่า C ++ ของ java คือinstanceof
อะไร
คำตอบ:
ลองใช้:
if(NewType* v = dynamic_cast<NewType*>(old)) {
// old was safely casted to NewType
v->doSomething();
}
สิ่งนี้ต้องการคอมไพเลอร์ของคุณเพื่อเปิดใช้งานการสนับสนุน rtti
แก้ไข: ฉันมีความคิดเห็นที่ดีในคำตอบนี้!
ทุกครั้งที่คุณต้องใช้ dynamic_cast (หรืออินสแตนซ์ของ) คุณควรถามตัวเองว่ามันจำเป็นหรือไม่ โดยทั่วไปแล้วมันเป็นสัญลักษณ์ของการออกแบบที่ไม่ดี
วิธีแก้ปัญหาทั่วไปคือการใส่ลักษณะพิเศษสำหรับคลาสที่คุณกำลังตรวจสอบในฟังก์ชันเสมือนบนคลาสพื้นฐานหรืออาจแนะนำสิ่งที่คล้ายกับผู้เยี่ยมชมซึ่งคุณสามารถแนะนำพฤติกรรมที่เฉพาะเจาะจงสำหรับคลาสย่อยโดยไม่ต้องเปลี่ยนอินเทอร์เฟซ หลักสูตร)
ตามที่ระบุไว้ dynamic_cast ไม่ได้มาฟรี การแฮ็กที่เรียบง่ายและมีประสิทธิภาพสม่ำเสมอที่จัดการได้มากที่สุด (แต่ไม่ใช่ทุกกรณี) นั้นเป็นการเพิ่ม enum ที่เป็นตัวแทนประเภทที่เป็นไปได้ทั้งหมดของคุณและตรวจสอบว่าคุณได้รับสิ่งที่ถูกต้องหรือไม่
if(old->getType() == BOX) {
Box* box = static_cast<Box*>(old);
// Do something box specific
}
นี่ไม่ใช่การออกแบบ oo ที่ดี แต่สามารถแก้ไขได้และค่าใช้จ่ายจะมากหรือน้อยเพียงการเรียกฟังก์ชันเสมือน มันยังใช้งานได้ไม่ว่า 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
}
ขึ้นอยู่กับสิ่งที่คุณต้องการคุณสามารถทำได้:
template<typename Base, typename T>
inline bool instanceof(const T*) {
return std::is_base_of<Base, T>::value;
}
ใช้:
if (instanceof<BaseClass>(ptr)) { ... }
อย่างไรก็ตามสิ่งนี้จะทำงานกับชนิดที่คอมไพเลอร์รู้จัก
แก้ไข:
รหัสนี้ควรใช้ได้กับพอยน์เตอร์พอยน์เตอร์:
template<typename Base, typename T>
inline bool instanceof(const T *ptr) {
return dynamic_cast<const Base*>(ptr) != nullptr;
}
ตัวอย่าง: http://cpp.sh/6qir
ฉันคิดว่าคำถามนี้ยังคงเกี่ยวข้องในวันนี้ การใช้มาตรฐาน C ++ 11 ตอนนี้คุณสามารถใช้instanceof
ฟังก์ชันได้โดยไม่ต้องใช้dynamic_cast
สิ่งนี้:
if (dynamic_cast<B*>(aPtr) != nullptr) {
// aPtr is instance of B
} else {
// aPtr is NOT instance of B
}
แต่คุณยังคงเชื่อมั่นในRTTI
การสนับสนุน ดังนั้นนี่คือทางออกสำหรับปัญหานี้ขึ้นอยู่กับ Macros และ Metaprogramming Magic ข้อเสียเปรียบเท่านั้น IMHO คือว่าวิธีนี้ไม่ได้ทำงานให้กับมรดกหลาย
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
แต่กระแสน้ำจะเปลี่ยนโดยการปรับไบนารีของเราโดยใช้การคอมไพล์ otpimization คำสั่งนั้นเป็นคอมไพเลอร์ ผลลัพธ์ที่ได้จากเครื่องท้องถิ่นของฉันนั้นยอดเยี่ยมมาก:A
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 และเทมเพลต metaprogramming และไม่ขี้เกียจเกินกว่าที่จะเพิ่มคำแนะนำเล็ก ๆ ให้กับคลาสของคลาสลำดับชั้นของคุณวิธีนี้สามารถเพิ่มแอปพลิเคชันของคุณเล็กน้อย ด้วยความเคารพต่อประสิทธิภาพหากคุณมักจะจบลงด้วยการตรวจสอบอินสแตนซ์ของตัวชี้ แต่ใช้ด้วยความระมัดระวัง ไม่มีการรับประกันความถูกต้องของวิธีการนี้
หมายเหตุ: การสาธิตทั้งหมดได้รับการคอมไพล์โดยใช้clang (Apple LLVM version 9.0.0 (clang-900.0.39.2))
ภายใต้ macOS Sierra ใน MacBook Pro Mid 2012
แก้ไข:
ฉันได้ยังผ่านการทดสอบประสิทธิภาพการทำงานบนเครื่อง 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
dynamic_cast
เป็นที่รู้กันว่าไม่มีประสิทธิภาพ มันสำรวจลำดับชั้นการสืบทอดและเป็นทางออกเดียวถ้าคุณมีการสืบทอดหลายระดับและจำเป็นต้องตรวจสอบว่าวัตถุเป็นตัวอย่างของประเภทใดประเภทหนึ่งในลำดับชั้นชนิดหรือไม่
แต่ถ้ารูปแบบที่ จำกัด มากขึ้นinstanceof
นั้นจะตรวจสอบเฉพาะว่าวัตถุนั้นเป็นประเภทที่คุณระบุอย่างแน่นอนเพียงพอสำหรับความต้องการของคุณหรือไม่ฟังก์ชันด้านล่างจะมีประสิทธิภาพมากขึ้น:
template<typename T, typename K>
inline bool isType(const K &k) {
return typeid(T).hash_code() == typeid(k).hash_code();
}
นี่คือตัวอย่างของวิธีที่คุณเรียกใช้ฟังก์ชันด้านบน:
DerivedA k;
Base *p = &k;
cout << boolalpha << isType<DerivedA>(*p) << endl; // true
cout << boolalpha << isType<DerivedB>(*p) << endl; // false
คุณจะระบุประเภทเทมเพลตA
(เป็นประเภทที่คุณกำลังตรวจสอบ) และส่งผ่านวัตถุที่คุณต้องการทดสอบเป็นอาร์กิวเมนต์ (จากประเภทเทมเพลตที่K
จะอนุมาน)
#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);
}
instanceof
เคียวรีชนิดไดนามิก แต่ในคำตอบนี้ประเภทไดนามิกและคงที่สอดคล้องกันเสมอ
สิ่งนี้ทำงานได้สมบูรณ์แบบสำหรับฉันโดยใช้ Code :: Blocks IDE กับ GCC complier
#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;
}
typeid
" ซึ่งในขณะที่ผิด ("ไม่มีการรับประกันว่าอินสแตนซ์ std :: type_info เดียวกันจะถูกอ้างอิงโดยการประเมินทั้งหมดของการแสดงออกของ typeid ในประเภทเดียวกัน ... assert(typeid(A) == typeid(A)); /* not guaranteed */
" ดูcppreference.com ) บ่งบอกว่าเขาทำอย่างน้อยพยายามตอบคำถามถ้าไม่ช่วยเหลือเพราะเขาละเลยที่จะเสนอตัวอย่างการทำงานที่น้อยที่สุด