C ++ 'da bir nesnenin türünü bulma


151

Bir A sınıfım ve ondan miras alan başka bir sınıfım var, B, A tipi bir nesneyi parametre olarak kabul eden bir işlevi geçersiz kılıyorum, bu yüzden bir A'yı kabul etmem gerekiyor. Ancak, daha sonra yalnızca B'nin sahip olduğu işlevleri çağırıyorum, bu yüzden yanlış döndürmek ve iletilen nesne B tipi değilse devam etmek istemiyorum.

İşlevime iletilen nesnenin hangi tür olduğunu bulmanın en iyi yolu nedir?


Bunların YOK kök sorunu ... ele görünmektedircode void *p1 = new int(); void *p2 = new double(); void f(void *p) {....code goes here } f(p1) should print int... f(p2) should print double...
David V. Corbin

Yanıtlar:


168

dynamic_cast hile yapmalı

TYPE& dynamic_cast<TYPE&> (object);
TYPE* dynamic_cast<TYPE*> (object);

dynamic_castAnahtar kelime döküm geçerliliğini sağlamak için bir çalışma zamanı kontrolü yaptıktan, başka bir işaretçi veya referans türünden bir referans noktasını düşürdü.

Gerçek nesne türü olmayan bir türe işaretçi çevirmeye çalışırsanız, çevrimin sonucu NULL olacaktır. Gerçek nesne türü olmayan bir türe gönderme yapmaya çalışırsanız, çevrim bir bad_castistisna atar .

Dynamic_cast'in çalışması için Base sınıfında en az bir sanal işlev olduğundan emin olun.

Wikipedia konusu Çalışma zamanı türü bilgisi

RTTI, yalnızca polimorfik sınıflar için kullanılabilir, yani en az bir sanal yöntemi vardır. Uygulamada, bu bir sınırlama değildir çünkü temel sınıfların, bir temel işaretçiden silindikleri takdirde, türetilmiş sınıfların nesnelerinin düzgün temizleme gerçekleştirmesine izin vermek için sanal bir yıkıcıya sahip olması gerekir.


1
Dynamic_cast'in çalışması için Base sınıfında sanal bir işlev olması gerektiğiyle ne demek istiyorsun? Bu bana önemli görünüyor, sadece tahmin edeceğim.
GiCo

3
Tamam buldu: Çalışma Zamanı Türü Bilgisi (RTTI) yalnızca polimorfik sınıflar için kullanılabilir, yani en az bir sanal yöntemi vardır. dynamic_cast ve typeid için RTTI gerekir.
GiCo

Does not dynamic_castDönüştürülebilir değilse atmak? Bir atış yapmadan bunu yapmanın bir yolu var mı?
jww

A* aptr = dynamic_cast<A*>(ptr);// böyle olması gerekmiyor mu
Mehdi Karamosly

Bu, a'ya dönüştürülmüş POD'lar için çalışıyor mu uint8_t*? Olduğunu, bunu kontrol edebilirsiniz uint32_t* x = dynamic_cast<uint32_t*>(p), nerede polduğunu uint8_t*? (Hırsızlık ihlalleri için bir test bulmaya çalışıyorum).
jww

159

Dinamik döküm, problem tanımınız için en iyisidir, ancak sınıf türünü şu şekilde bulabileceğinizi eklemek istiyorum:

#include <typeinfo>

...
string s = typeid(YourClass).name()

4
Gerçekten nesnenizin ne olduğunu bilmiyorsanız iyi. Kabul edilen cevap, yaptığınızı varsayar.
2012'de

4
@xus Evet. standart başlıkların bir parçası
Amey Jah

8
Nasıl olduğunu anlamıyorum . Tür kimliği adlarının yararlı olması gerekli değildir ve uygulama tanımlıdır.
Ayakkabı

3
Burada en ilginç olanı: Aynı sınıftaki örneklerin adlarının eşit olması gerekmez. Bununla birlikte, typeid'in kendisi aynı sınıftaki örnekler için eşit karşılaştırması yapmak zorundadır, bkz. Stackoverflow.com/questions/1986418/typeid-versus-typeof-in-c
FourtyTwo

1
Gcc'nin magled adını döndürdüğüne dikkat edin, örn 11MyClass. Mangle'ı kaldırmak için, içindeki ABI uzantı kitaplığını kullanabilirsiniz cxxabi.h. Bu size abi::__cxa_demanglegerçek adı verecek olanı verir
David G

29

Buna RTTI denir , ancak neredeyse kesinlikle tasarımınızı burada yeniden gözden geçirmek istersiniz, çünkü türü bulmak ve ona göre özel şeyler yapmak kodunuzu daha kırılgan hale getirir.


5
Doğru. Maalesef mevcut bir proje üzerinde çalışıyorum, bu yüzden tasarımı veya A sınıfındaki herhangi bir şeyi gerçekten değiştiremem
lemnisca

12

Tamamlamak için, Robocide'den oluşturacağım ve bunun typeidname () kullanmadan tek başına kullanılabileceğini göstereceğim :

#include <typeinfo>
#include <iostream>

using namespace std;

class A {
public:
    virtual ~A() = default; // We're not polymorphic unless we
                            // have a virtual function.
};
class B : public A { } ;
class C : public A { } ;

int
main(int argc, char* argv[])
{
    B b;
    A& a = b;

    cout << "a is B: " << boolalpha << (typeid(a) == typeid(B)) << endl;
    cout << "a is C: " << boolalpha << (typeid(a) == typeid(C)) << endl;
    cout << "b is B: " << boolalpha << (typeid(b) == typeid(B)) << endl;
    cout << "b is A: " << boolalpha << (typeid(b) == typeid(A)) << endl;
    cout << "b is C: " << boolalpha << (typeid(b) == typeid(C)) << endl;
}

Çıktı:

a is B: true
a is C: false
b is B: true
b is A: false
b is C: false

9

Muhtemelen nesnelerinize bir kimlik "etiketi" gömün ve bunu A sınıfı nesneler ile B sınıfı nesneler arasında ayrım yapmak için kullanın.

Ancak bu, tasarımda bir kusur olduğunu gösterir. İdeal olarak, B'de A'nın sahip olmadığı yöntemler, A'nın bir parçası olmalı, ancak boş bırakılmalıdır ve B bunların üzerine yazar. Bu, sınıfa özgü kodu ortadan kaldırır ve daha çok OOP ruhuna uygundur.



4

Çünkü sınıfınız polimorfik değil. Deneyin:

struct BaseClas { int base; virtual ~BaseClas(){} };
class Derived1 : public BaseClas { int derived1; };

Şimdi BaseClaspolimorfik. Sınıfı struct olarak değiştirdim çünkü bir yapının üyeleri varsayılan olarak geneldir.


3

Açıklamanız biraz kafa karıştırıcı.

Genel olarak konuşursak, bazı C ++ uygulamalarının bunun için mekanizmaları olmasına rağmen, tür hakkında soru sormanız gerekmez. Bunun yerine, A'ya gösterici üzerinde bir dynamic_cast yapmanız gerekir. Bunun yapacağı şey, çalışma zamanında, A'ya göstericinin gerçek içeriğinin kontrol edilmesidir. Eğer bir B'niz varsa, işaretçinizi B'ye götürürsünüz. Aksi takdirde, bir istisna veya sıfır alırsınız.


1
Yalnızca başarısız olan bir referans atımı gerçekleştirirseniz bir istisna alacağınız unutulmamalıdır . yani dinamik_ yayın <T &> [t). Başarısız işaretçi NULL döndürür. ie dynamic_cast <T *> [t)
AlfaZulu

Evet, bunu daha iyi açıklamalıydım. Teşekkürler. Keşke, C tiplerinde tanımlanacak bir kelimenin değer yerine referans olarak verildiği bir kelime olsaydı.
Uri

3

Diğerlerinin belirttiği gibi dynamic_cast kullanabilirsiniz. Ancak üzerinde çalıştığınız türetilmiş sınıfın türünü bulmak için genellikle dynamic_cast kullanmak kötü tasarıma işaret eder. Parametre olarak A işaretçisini alan bir işlevi geçersiz kılıyorsanız, A sınıfının kendi yöntemleriyle / verileriyle çalışabilmelidir ve B sınıfının verilerine bağlı olmamalıdır. yazdığınız yöntemin yalnızca B sınıfıyla çalışacağından eminseniz, B sınıfına yeni bir yöntem yazmalısınız.


1

Aşırı yüklenmiş fonksiyonları kullanın. Dynamic_cast veya hatta RTTI desteği gerektirmez:

class A {};
class B : public A {};

class Foo {
public:
    void Bar(A& a) {
        // do something
    }
    void Bar(B& b) {
        Bar(static_cast<A&>(b));
        // do B specific stuff
    }
};

Orijinal sorudan: "Daha sonra yalnızca B'nin sahip olduğu işlevleri çağırırım" - böyle bir durumda aşırı yükleme nasıl çalışır?
Marcin Gil

Bar'ı A ile aradığınızda, B meselesi olmaz. Bar'ı bir B ile çağırdığınızda, yalnızca B'de var olan yöntemler çağrılabilir. Orijinal soruyu okuyor musunuz? Bar, "A tipi bir nesneyi parametre olarak kabul eden bir işlevi geçersiz
kılıyorum

7
Bu, sorgulayıcının kullandığından şüphelendiğim dinamik polimorfizm ile çalışmıyor. C ++ parametrenin çalışma zamanı sınıfına dayalı bir aşırı yük seçemez, yalnızca derleme zamanı türüne göre.
Steve Jessop

1

Yükseltme kitaplığına erişebiliyorsanız, belki ihtiyacınız olan type_id_with_cvr () işlevi, const, volatile, & ve && değiştiricilerini kaldırmadan veri türü sağlayabilir . İşte C ++ 11'de basit bir örnek:

#include <iostream>
#include <boost/type_index.hpp>

int a;
int& ff() 
{
    return a;
}

int main() {
    ff() = 10;
    using boost::typeindex::type_id_with_cvr;
    std::cout << type_id_with_cvr<int&>().pretty_name() << std::endl;
    std::cout << type_id_with_cvr<decltype(ff())>().pretty_name() << std::endl;
    std::cout << typeid(ff()).name() << std::endl;
}

Umarım bu yararlıdır.

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.