Phương pháp ưa thích để đạt được C ++ tương đương với java là instanceof
gì?
Phương pháp ưa thích để đạt được C ++ tương đương với java là instanceof
gì?
Câu trả lời:
Hãy thử sử dụng:
if(NewType* v = dynamic_cast<NewType*>(old)) {
// old was safely casted to NewType
v->doSomething();
}
Điều này đòi hỏi trình biên dịch của bạn phải kích hoạt hỗ trợ rtti.
EDIT: Tôi đã có một số ý kiến tốt về câu trả lời này!
Mỗi khi bạn cần sử dụng một Dynamic_cast (hoặc thể hiện), bạn nên tự hỏi mình xem đó có phải là điều cần thiết không. Nó thường là một dấu hiệu của thiết kế kém.
Cách giải quyết điển hình là đưa hành vi đặc biệt cho lớp bạn đang kiểm tra vào một hàm ảo trên lớp cơ sở hoặc có thể giới thiệu một cái gì đó giống như khách truy cập nơi bạn có thể giới thiệu hành vi cụ thể cho các lớp con mà không thay đổi giao diện (ngoại trừ thêm giao diện chấp nhận của khách truy cập khóa học).
Như đã chỉ ra Dynamic_cast không miễn phí. Một hack đơn giản và thực hiện nhất quán xử lý hầu hết (nhưng không phải tất cả các trường hợp) về cơ bản là thêm một enum đại diện cho tất cả các loại có thể mà lớp của bạn có thể có và kiểm tra xem bạn có chọn đúng không.
if(old->getType() == BOX) {
Box* box = static_cast<Box*>(old);
// Do something box specific
}
Đây không phải là thiết kế oo tốt, nhưng nó có thể là một cách giải quyết và chi phí của nó ít nhiều chỉ bằng một cuộc gọi chức năng ảo. Nó cũng hoạt động bất kể RTTI có được kích hoạt hay không.
Lưu ý rằng phương pháp này không hỗ trợ nhiều cấp thừa kế, vì vậy nếu bạn không cẩn thận, bạn có thể kết thúc bằng mã trông như thế này:
// 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
}
Tùy thuộc vào những gì bạn muốn làm, bạn có thể làm điều này:
template<typename Base, typename T>
inline bool instanceof(const T*) {
return std::is_base_of<Base, T>::value;
}
Sử dụng:
if (instanceof<BaseClass>(ptr)) { ... }
Tuy nhiên, điều này hoàn toàn hoạt động trên các loại như trình biên dịch đã biết.
Biên tập:
Mã này sẽ làm việc cho các con trỏ đa hình:
template<typename Base, typename T>
inline bool instanceof(const T *ptr) {
return dynamic_cast<const Base*>(ptr) != nullptr;
}
Ví dụ: http://cpp.sh/6qir
Tôi nghĩ rằng câu hỏi này vẫn còn liên quan đến ngày hôm nay. Sử dụng tiêu chuẩn C ++ 11, giờ đây bạn có thể thực hiện một instanceof
chức năng mà không cần sử dụng dynamic_cast
như thế này:
if (dynamic_cast<B*>(aPtr) != nullptr) {
// aPtr is instance of B
} else {
// aPtr is NOT instance of B
}
Nhưng bạn vẫn phụ thuộc vào RTTI
hỗ trợ. Vì vậy, đây là giải pháp của tôi cho vấn đề này tùy thuộc vào một số Macros và Metaprogramming Magic. Hạn chế duy nhất imho là cách tiếp cận này không hoạt động cho nhiều kế thừa .
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());
Sau đó, bạn có thể sử dụng công cụ này ( một cách thận trọng ) như sau:
DemoClassHVELy.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)
Đoạn mã sau trình bày một bản demo nhỏ để xác minh hành vi thô sơ.
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;
}
Đầu ra:
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
Câu hỏi thú vị nhất hiện nay là, nếu thứ xấu xa này hiệu quả hơn việc sử dụng dynamic_cast
. Do đó, tôi đã viết một ứng dụng đo lường hiệu suất rất cơ bản.
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;
}
Các kết quả khác nhau và về cơ bản dựa trên mức độ tối ưu hóa trình biên dịch. Biên dịch chương trình đo hiệu suất bằng cách sử dụng g++ -std=c++11 -O0 -o instanceof-performance InstanceOfPerformance.cpp
đầu ra trên máy cục bộ của tôi là:
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, kết quả này rất tỉnh táo, bởi vì thời gian chứng minh rằng cách tiếp cận mới không nhanh hơn nhiều so với dynamic_cast
cách tiếp cận. Nó thậm chí còn kém hiệu quả hơn đối với trường hợp thử nghiệm đặc biệt sẽ kiểm tra xem một con trỏ của A
là một thể hiện của A
. NHƯNG thủy triều xoay bằng cách điều chỉnh nhị phân của chúng tôi bằng cách sử dụng otpimization trình biên dịch. Lệnh biên dịch tương ứng là g++ -std=c++11 -O3 -o instanceof-performance InstanceOfPerformance.cpp
. Kết quả trên máy cục bộ của tôi thật tuyệt vời:
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
Nếu bạn không phụ thuộc vào nhiều kế thừa, không phải là đối thủ của các macro C, RTTI và siêu lập trình mẫu cũ và không quá lười biếng để thêm một số hướng dẫn nhỏ vào các lớp của hệ thống phân cấp lớp của bạn, thì cách tiếp cận này có thể tăng cường ứng dụng của bạn một chút đối với hiệu suất của nó, nếu bạn thường kết thúc bằng việc kiểm tra thể hiện của một con trỏ. Nhưng sử dụng nó một cách thận trọng . Không có bảo hành cho tính chính xác của phương pháp này.
Lưu ý: Tất cả các bản demo được biên dịch bằng clang (Apple LLVM version 9.0.0 (clang-900.0.39.2))
macOS Sierra trên MacBook Pro Mid 2012.
Chỉnh sửa:
Tôi cũng đã kiểm tra hiệu năng trên máy Linux bằng cách sử dụng gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609
. Trên nền tảng này, lợi ích của nước hoa không quá quan trọng như trên macOs với tiếng kêu.
Đầu ra (không có tối ưu hóa trình biên dịch):
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
Đầu ra (với tối ưu hóa trình biên dịch):
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
được biết là không hiệu quả. Nó truyền qua hệ thống phân cấp thừa kế và là giải pháp duy nhất nếu bạn có nhiều cấp kế thừa và cần kiểm tra xem một đối tượng có phải là một thể hiện của bất kỳ một trong các loại trong phân cấp loại của nó không.
Nhưng nếu một dạng hạn chế hơn instanceof
chỉ kiểm tra xem một đối tượng có chính xác là loại bạn chỉ định, đủ cho nhu cầu của bạn hay không, thì chức năng dưới đây sẽ hiệu quả hơn rất nhiều:
template<typename T, typename K>
inline bool isType(const K &k) {
return typeid(T).hash_code() == typeid(k).hash_code();
}
Đây là một ví dụ về cách bạn gọi hàm trên:
DerivedA k;
Base *p = &k;
cout << boolalpha << isType<DerivedA>(*p) << endl; // true
cout << boolalpha << isType<DerivedB>(*p) << endl; // false
Bạn sẽ chỉ định loại mẫu A
(như loại bạn đang kiểm tra) và chuyển vào đối tượng bạn muốn kiểm tra làm đối số (từ đó loại mẫu K
sẽ được suy ra).
#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
truy vấn kiểu động, nhưng trong câu trả lời này, kiểu động và tĩnh luôn tương ứng.
Điều này hoạt động hoàn hảo đối với tôi khi sử dụng Code :: Blocks IDE với trình biên dịch GCC
#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
", trong khi sai ("Không có gì đảm bảo rằng cùng một trường hợp std :: type_info sẽ được tham chiếu bởi tất cả các đánh giá của biểu thức typeid trên cùng loại ... assert(typeid(A) == typeid(A)); /* not guaranteed */
", xem cppreference.com ), không chỉ ra rằng anh ta ít nhất đã cố gắng trả lời câu hỏi, nếu không có ích vì anh ta đã bỏ qua việc đưa ra một ví dụ làm việc tối thiểu.