Apa metode yang disukai untuk mencapai setara C ++ dari java instanceof
?
Apa metode yang disukai untuk mencapai setara C ++ dari java instanceof
?
Jawaban:
Coba gunakan:
if(NewType* v = dynamic_cast<NewType*>(old)) {
// old was safely casted to NewType
v->doSomething();
}
Ini membutuhkan kompiler Anda untuk mengaktifkan dukungan rtti.
EDIT: Saya punya beberapa komentar bagus tentang jawaban ini!
Setiap kali Anda perlu menggunakan dynamic_cast (atau instanceof) Anda sebaiknya bertanya pada diri sendiri apakah itu hal yang perlu. Ini umumnya merupakan tanda dari desain yang buruk.
Pemecahan umum adalah menempatkan perilaku khusus untuk kelas yang Anda periksa menjadi fungsi virtual di kelas dasar atau mungkin memperkenalkan sesuatu seperti pengunjung mana Anda dapat memperkenalkan perilaku spesifik untuk subkelas tanpa mengubah antarmuka (kecuali untuk menambahkan antarmuka penerimaan pengunjung dari tentu saja).
Seperti yang ditunjukkan dynamic_cast tidak datang secara gratis. Peretasan sederhana dan secara konsisten melakukan yang menangani sebagian besar (tetapi tidak semua kasus) pada dasarnya menambahkan enum yang mewakili semua jenis yang mungkin dimiliki kelas Anda dan memeriksa apakah Anda mendapatkannya dengan benar.
if(old->getType() == BOX) {
Box* box = static_cast<Box*>(old);
// Do something box specific
}
Ini bukan desain yang bagus, tetapi bisa menjadi solusi dan biayanya lebih atau kurang hanya panggilan fungsi virtual. Ini juga berfungsi terlepas dari RTTI diaktifkan atau tidak.
Perhatikan bahwa pendekatan ini tidak mendukung banyak level pewarisan jadi jika Anda tidak berhati-hati, Anda mungkin berakhir dengan kode yang terlihat seperti ini:
// 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
}
Bergantung pada apa yang ingin Anda lakukan, Anda bisa melakukan ini:
template<typename Base, typename T>
inline bool instanceof(const T*) {
return std::is_base_of<Base, T>::value;
}
Menggunakan:
if (instanceof<BaseClass>(ptr)) { ... }
Namun, ini murni beroperasi pada tipe yang dikenal oleh kompiler.
Edit:
Kode ini harus berfungsi untuk petunjuk polimorfik:
template<typename Base, typename T>
inline bool instanceof(const T *ptr) {
return dynamic_cast<const Base*>(ptr) != nullptr;
}
Contoh: http://cpp.sh/6qir
Saya pikir pertanyaan ini masih relevan sampai sekarang. Menggunakan standar C ++ 11 Anda sekarang dapat mengimplementasikan instanceof
fungsi tanpa menggunakan dynamic_cast
seperti ini:
if (dynamic_cast<B*>(aPtr) != nullptr) {
// aPtr is instance of B
} else {
// aPtr is NOT instance of B
}
Tetapi Anda masih bergantung pada RTTI
dukungan. Jadi di sini adalah solusi saya untuk masalah ini tergantung pada beberapa Macro dan Metaprogramming Magic. Satu-satunya kelemahan imho adalah bahwa pendekatan ini tidak berfungsi untuk pewarisan berganda .
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());
Anda kemudian dapat menggunakan hal-hal ini ( dengan hati-hati ) sebagai berikut:
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)
Kode berikut menyajikan demo kecil untuk memverifikasi dasar perilaku yang benar.
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;
}
Keluaran:
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
Pertanyaan paling menarik yang sekarang muncul adalah, apakah hal jahat ini lebih efisien daripada penggunaan dynamic_cast
. Karena itu saya telah menulis aplikasi pengukuran kinerja yang sangat mendasar.
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;
}
Hasilnya bervariasi dan pada dasarnya didasarkan pada tingkat optimisasi kompiler. Mengkompilasi program pengukuran kinerja menggunakan g++ -std=c++11 -O0 -o instanceof-performance InstanceOfPerformance.cpp
output pada mesin lokal saya adalah:
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
Mhm, hasil ini sangat serius, karena timing menunjukkan bahwa pendekatan baru tidak jauh lebih cepat dibandingkan dengan dynamic_cast
pendekatan. Ini bahkan kurang efisien untuk kasus uji khusus yang menguji apakah pointer A
adalah turunan dari A
. TAPI ombak berubah dengan menyetel biner kami menggunakan otpimisasi kompiler. Perintah kompiler masing-masing adalah g++ -std=c++11 -O3 -o instanceof-performance InstanceOfPerformance.cpp
. Hasilnya di mesin lokal saya sangat mengagumkan:
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
Jika Anda tidak bergantung pada multiple inheritance, tidak ada lawan dari macro C tua yang baik, RTTI dan metaprogramming template dan tidak terlalu malas untuk menambahkan beberapa instruksi kecil ke kelas-kelas hierarki kelas Anda, maka pendekatan ini dapat meningkatkan aplikasi Anda sedikit sehubungan dengan kinerjanya, jika Anda sering berakhir dengan memeriksa contoh pointer. Tapi gunakan dengan hati-hati . Tidak ada jaminan untuk kebenaran pendekatan ini.
Catatan: Semua demo dikompilasi menggunakan di clang (Apple LLVM version 9.0.0 (clang-900.0.39.2))
bawah macOS Sierra pada MacBook Pro Mid 2012.
Sunting:
Saya juga telah menguji kinerja menggunakan mesin Linux gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609
. Pada platform ini, manfaat kinerja tidak begitu signifikan seperti pada macOs dengan dentang.
Output (tanpa optimasi kompiler):
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
Output (dengan optimisasi kompiler):
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
diketahui tidak efisien. Itu melintasi hierarki warisan, dan itu adalah satu-satunya solusi jika Anda memiliki beberapa tingkat warisan, dan perlu memeriksa apakah suatu objek adalah instance dari salah satu dari jenis dalam hierarki tipenya.
Tetapi jika bentuk yang lebih terbatas dari instanceof
itu hanya memeriksa apakah suatu objek persis dengan jenis yang Anda tentukan, cukup untuk kebutuhan Anda, fungsi di bawah ini akan jauh lebih efisien:
template<typename T, typename K>
inline bool isType(const K &k) {
return typeid(T).hash_code() == typeid(k).hash_code();
}
Berikut ini contoh cara Anda menjalankan fungsi di atas:
DerivedA k;
Base *p = &k;
cout << boolalpha << isType<DerivedA>(*p) << endl; // true
cout << boolalpha << isType<DerivedB>(*p) << endl; // false
Anda akan menentukan jenis templat A
(sebagai jenis yang Anda periksa), dan meneruskan objek yang ingin Anda uji sebagai argumen (dari jenis templat mana yang K
akan disimpulkan).
#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
menanyakan tipe dinamis, tetapi dalam jawaban ini tipe dinamis dan statis selalu sesuai.
Ini bekerja sempurna untuk saya menggunakan Code :: Blocks IDE dengan 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
", yang sementara salah ("Tidak ada jaminan bahwa tur std :: type_info yang sama akan dirujuk oleh semua evaluasi ekspresi typeid pada tipe yang sama ... assert(typeid(A) == typeid(A)); /* not guaranteed */
", lihat cppreference.com ), memang menunjukkan bahwa ia setidaknya mencoba menjawab pertanyaan, jika tidak membantu karena ia lalai menawarkan contoh kerja minimal.