Java örneğinin C ++ eşdeğeri


202

Java'nın C ++ eşdeğerini elde etmek için tercih edilen yöntem nedir instanceof?


57
Performans ve uyumluluk tarafından tercih edilen ...
Yuval Adam

7
"anında - hangi dilde" diye sormak adil değil mi?
mysticcoder

3
@ mysticcoder: Bulgarca için "например на" alıyorum, GT yine de C ++ 'ı desteklemiyor
Mark K Cowan

Yanıtlar:


200

Kullanmayı deneyin:

if(NewType* v = dynamic_cast<NewType*>(old)) {
   // old was safely casted to NewType
   v->doSomething();
}

Bu, derleyicinizin rtti desteğinin etkinleştirilmesini gerektirir.

EDIT: Bu cevap hakkında bazı iyi yorumlar var!

Bir dynamic_cast (veya instanceof) kullanmanız gerektiğinde, kendinize bunun gerekli bir şey olup olmadığını sormanız daha iyi olur. Genellikle zayıf tasarımın bir işaretidir.

Tipik geçici çözümler, kontrol ettiğiniz sınıf için özel davranışı temel sınıfta sanal bir işleve koymak veya belki de ziyaretçi gibi bir şey tanıtmaktır arabirimi değiştirmeden alt sınıflar için belirli davranışlar ekleyebileceğiniz tanıtmaktır ( elbette).

Belirtildiği gibi dynamic_cast ücretsiz gelmiyor. Çoğu (ancak her durumda değil) işleyen basit ve sürekli olarak çalışan bir kesmek temel olarak sınıfınızın sahip olabileceği tüm olası türleri temsil eden bir enum eklemek ve doğru olanı alıp almadığınızı kontrol etmektir.

if(old->getType() == BOX) {
   Box* box = static_cast<Box*>(old);
   // Do something box specific
}

Bu iyi oo tasarımı değil, ancak bir geçici çözüm olabilir ve maliyeti az ya da çok sadece bir sanal işlev çağrısıdır. Ayrıca RTTI etkin olsun ya da olmasın çalışır.

Bu yaklaşımın birden fazla kalıtım düzeyini desteklemediğini unutmayın, bu yüzden dikkatli değilseniz, aşağıdaki gibi kodla bitebilirsiniz:

// 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
}

4
Genelde "instanceof" çekini yaptığınızda durum böyle
Laserallan

7
İnstanceof kullanmanız gerekiyorsa, çoğu durumda tasarımınızda bir sorun vardır.
mslot

24
Dynamic_cast'in yüksek maliyetli bir işlem olduğunu unutmayın.
Klaim

13
Dinamik tip testinin makul kullanımlarının birçok örneği vardır. Genellikle tercih edilmez, ancak bir yeri vardır. (Aksi takdirde, neden veya eşdeğeri her büyük OO dilinde görünecektir? C ++, Java, Python, vb.)
Paul Draper

2
Farklı şekilde ele alınmaları gerekmiyorsa her ikisini de IOException düzeyinde yakalarım. Farklı bir şekilde ele alınması gerekiyorsa, her istisna için bir catch bloğu eklerim.
mslot

37

Ne yapmak istediğinize bağlı olarak bunu yapabilirsiniz:

template<typename Base, typename T>
inline bool instanceof(const T*) {
    return std::is_base_of<Base, T>::value;
}

kullanın:

if (instanceof<BaseClass>(ptr)) { ... }

Ancak, bu sadece derleyici tarafından bilindiği gibi çalışır.

Düzenle:

Bu kod polimorfik işaretçiler için çalışmalıdır:

template<typename Base, typename T>
inline bool instanceof(const T *ptr) {
    return dynamic_cast<const Base*>(ptr) != nullptr;
}

Örnek: http://cpp.sh/6qir


Zarif ve iyi yapılmış bir çözüm. +1 Ama doğru işaretçiyi almaya dikkat edin. Polimorfik işaretçi için geçerli değil mi?
Adrian Maire

Bu işlevi kullanırken işaretçiyi ertelersek ne olur? Daha sonra polimorfik işaretçiler için işe yarar mı?
mark.kedzierski

Hayır, bu sadece derleyici tarafından bilinen tipler üzerinde çalışır. Kayıtsız olsanız da olmasanız da polimorfik işaretçilerle çalışmaz. Yine de bu durumda işe yarayabilecek bir şey ekleyeceğim.
panzi


Dinamik dökümün hedef türü neden "sabit"?
user1056903

7

Dynamic_cast olmadan uygulama örneği

Bu sorunun bugün hala geçerli olduğunu düşünüyorum. C ++ 11 standardını kullanarak artık böyle bir instanceofişlevi kullanmadan bir işlevi uygulayabilirsiniz dynamic_cast:

if (dynamic_cast<B*>(aPtr) != nullptr) {
  // aPtr is instance of B
} else {
  // aPtr is NOT instance of B
}

Ama hala RTTIdesteğe güveniyorsun . İşte bazı Makrolar ve Metaprogramming Magic bağlı olarak bu sorun için benim çözüm. Sadece imho bu yaklaşım yapmasıdır dezavantajı değil çalışmak çoklu miras .

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());

gösteri

Daha sonra bu şeyleri ( dikkatle ) aşağıdaki gibi kullanabilirsiniz:

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)

Aşağıdaki kod, temel davranışı doğrulamak için küçük bir demo sunar.

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;
}

Çıktı:

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

Verim

Şimdi ortaya çıkan en ilginç soru, bu kötü şeylerin kullanımından daha verimli olup olmadığıdır. dynamic_cast . Bu nedenle çok temel bir performans ölçüm uygulaması yazdım.

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;
}

Sonuçlar değişir ve temel olarak derleyici optimizasyon derecesine dayanır. g++ -std=c++11 -O0 -o instanceof-performance InstanceOfPerformance.cppYerel makinemdeki çıktıyı kullanarak performans ölçüm programını derlemek :

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, bu sonuç çok ayık, çünkü zamanlamalar yeni yaklaşımın yaklaşıma göre çok daha hızlı olmadığını gösteriyor dynamic_cast. İşaretçinin Abir örneği olup olmadığını test eden özel test durumu için bile daha az verimlidir A. ANCAK gelgit döner derleyici otpimization kullanarak bizim ikili ayarlayarak. İlgili derleyici komutu g++ -std=c++11 -O3 -o instanceof-performance InstanceOfPerformance.cpp. Yerel makinemdeki sonuç şaşırtıcıydı:

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

Birden fazla mirasa güvenmiyorsanız, eski C makrolarının, RTTI'nin ve şablon meta programlamasının rakibi değilseniz ve sınıf hiyerarşinizin sınıflarına bazı küçük talimatlar eklemek için çok tembel değilseniz, bu yaklaşım uygulamanızı biraz artırabilir performansıyla ilgili olarak, sık sık bir işaretçi örneğini kontrol ederseniz. Ancak dikkatli kullanın . Bu yaklaşımın doğruluğu için garanti yoktur.

Not: Tüm demolar clang (Apple LLVM version 9.0.0 (clang-900.0.39.2))2012 MacBook Pro Mid'de macOS Sierra kullanılarak derlenmiştir .

Düzenleme: Ayrıca kullanarak bir Linux makinede performans test ettik gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609. Bu platformda perfomance yararı, clanglı macO'larda olduğu kadar önemli değildi.

Çıktı (derleyici optimizasyonu olmadan):

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

Çıktı (derleyici optimizasyonu ile):

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

İyi düşünülmüş cevap! Zamanlamaları sağladığınıza sevindim. Bu ilginç bir okumaydı.
Eric

0

dynamic_castverimsiz olduğu bilinmektedir. Bu miras hiyerarşisini yukarı erişir ve o sadece miras çoklu düzeyde varsa çözüm ve bir nesne onun tipi hiyerarşisindeki türlerinin herhangi birinin örneğidir olup olmadığını kontrol etmek gerekir.

Ancak bunun daha sınırlı bir şekli, instanceofyalnızca bir nesnenin tam olarak belirttiğiniz tür olup olmadığını, ihtiyaçlarınız için yeterli olup olmadığını kontrol ederse, aşağıdaki işlev çok daha verimli olacaktır:

template<typename T, typename K>
inline bool isType(const K &k) {
    return typeid(T).hash_code() == typeid(k).hash_code();
}

Yukarıdaki işlevi nasıl çağıracağınıza ilişkin bir örnek:

DerivedA k;
Base *p = &k;

cout << boolalpha << isType<DerivedA>(*p) << endl;  // true
cout << boolalpha << isType<DerivedB>(*p) << endl;  // false

Şablon türünü A(kontrol ettiğiniz tür olarak) belirtir ve bağımsız değişken olarak test etmek istediğiniz nesneyi iletirsiniz (şablon türünün Kçıkartılacağı).


Standart, hash_code dosyasının farklı türler için benzersiz olmasını gerektirmez, bu nedenle bu güvenilir değildir.
mattnz

2
Typeid (T) 'nin kendisi eşitlikle karşılaştırılabilir değil mi, bu yüzden hashcode'a güvenmeye gerek yok mu?
Paul Stelian

-5
#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);
 }

1
Bu gerçekten kötü bir örnek. Neden aşırı yükleme kullanmıyorsunuz, bu daha ucuz?
user1095108

11
Asıl sorun, soruyu cevaplayamamasıdır. instanceofdinamik türü sorgular, ancak bu cevapta dinamik ve statik tür her zaman karşılık gelir.
MSalters

@HHH cevapladığınız soru sormanın yoludur!
programcı

-11

Bu Code :: Blocks IDE'yi GCC karşılaştırıcısı kullanarak benim için mükemmel çalıştı

#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;
}

1
@programmer @pgp'yi çağırmak istediğinizi düşünüyorum, kod biçimlendirmesini düzelttim. Ayrıca, cevabı temel olarak "kullanım typeid" gibi görünüyor (ki bu yanlışken ("Aynı std :: type_info örneğinin aynı tipteki typeid ifadesinin tüm değerlendirmelerinden bahsedileceğinin garantisi yoktur ... assert(typeid(A) == typeid(A)); /* not guaranteed */", bkz. cppreference.com ), asgari bir çalışma örneği sunmayı ihmal ettiği için yardımcı olmazsa en azından soruyu cevaplamaya çalıştığını gösterir.
Andres Riofrio
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.