나는 사용 라인을 따라 생각 typeid()
했지만 그 유형이 다른 클래스의 하위 클래스인지 묻는 방법을 모르겠습니다 (그런데 추상적입니다)
답변:
당신은 정말로해서는 안됩니다. 프로그램이 객체의 클래스를 알아야하는 경우 일반적으로 디자인 결함을 나타냅니다. 가상 기능을 사용하여 원하는 동작을 얻을 수 있는지 확인하십시오. 또한 수행하려는 작업에 대한 자세한 정보가 도움이 될 것입니다.
나는 당신이 다음과 같은 상황에 있다고 가정합니다.
class Base;
class A : public Base {...};
class B : public Base {...};
void foo(Base *p)
{
if(/* p is A */) /* do X */
else /* do Y */
}
이것이 당신이 가진 것이라면 다음과 같이 시도하십시오.
class Base
{
virtual void bar() = 0;
};
class A : public Base
{
void bar() {/* do X */}
};
class B : public Base
{
void bar() {/* do Y */}
};
void foo(Base *p)
{
p->bar();
}
편집 : 이 답변에 대한 논쟁이 수년이 지난 후에도 계속되고 있기 때문에 몇 가지 참고 자료를 던져야한다고 생각했습니다. 기본 클래스에 대한 포인터 또는 참조가 있고 코드에서 개체의 파생 클래스를 알아야하는 경우 Liskov 대체 원칙을 위반하는 것 입니다. 밥 삼촌 은 이것을 " 객체 지향 디자인에 대한 혐오 "라고 부릅니다 .
class Base
{
public: virtual ~Base() {}
};
class D1: public Base {};
class D2: public Base {};
int main(int argc,char* argv[]);
{
D1 d1;
D2 d2;
Base* x = (argc > 2)?&d1:&d2;
if (dynamic_cast<D2*>(x) == nullptr)
{
std::cout << "NOT A D2" << std::endl;
}
if (dynamic_cast<D1*>(x) == nullptr)
{
std::cout << "NOT A D1" << std::endl;
}
}
dynamic_cast<>
여기 가 필요 합니까? a는하지 않을까요 static_cast<>
충분?
x
컴파일 시간에 유형을 알 수 있습니까 ? 그렇다면 static_cast<>()
작동합니다. x
런타임까지 유형을 알 수 없으면 다음이 필요합니다dynamic_cast<>()
dynamic_cast
(적어도 다형성 유형의 경우) 와 함께 할 수 있습니다 .
사실, 다시 생각해 보면, 특정 유형인지 알 수 없습니다. dynamic_cast
해당 유형인지 하위 클래스인지는 알 수 있습니다.
template <class DstType, class SrcType>
bool IsType(const SrcType* src)
{
return dynamic_cast<const DstType*>(src) != nullptr;
}
std::is_polymorphic_v<T>
것입니다 false
.
아래 코드는이를 수행하는 세 가지 방법을 보여줍니다.
#include <iostream>
#include <typeinfo>
#include <typeindex>
enum class Type {Base, A, B};
class Base {
public:
virtual ~Base() = default;
virtual Type type() const {
return Type::Base;
}
};
class A : public Base {
Type type() const override {
return Type::A;
}
};
class B : public Base {
Type type() const override {
return Type::B;
}
};
int main()
{
const char *typemsg;
A a;
B b;
Base *base = &a; // = &b; !!!!!!!!!!!!!!!!!
Base &bbb = *base;
// below you can replace base with &bbb and get the same results
// USING virtual function
// ======================
// classes need to be in your control
switch(base->type()) {
case Type::A:
typemsg = "type A";
break;
case Type::B:
typemsg = "type B";
break;
default:
typemsg = "unknown";
}
std::cout << typemsg << std::endl;
// USING typeid
// ======================
// needs RTTI. under gcc, avoid -fno-rtti
std::type_index ti(typeid(*base));
if (ti == std::type_index(typeid(A))) {
typemsg = "type A";
} else if (ti == std::type_index(typeid(B))) {
typemsg = "type B";
} else {
typemsg = "unknown";
}
std::cout << typemsg << std::endl;
// USING dynamic_cast
// ======================
// needs RTTI. under gcc, avoid -fno-rtti
if (dynamic_cast</*const*/ A*>(base)) {
typemsg = "type A";
} else if (dynamic_cast</*const*/ B*>(base)) {
typemsg = "type B";
} else {
typemsg = "unknown";
}
std::cout << typemsg << std::endl;
}
위의 프로그램은 다음을 인쇄합니다.
type A
type A
type A
dynamic_cast
유형이 상속 계층 구조의 어디에서나 대상 유형을 포함하는지 확인할 수 있습니다 (예, and 에서 B
상속하면를 으로 직접 변환 할 수있는 잘 알려지지 않은 기능입니다 ). 개체의 정확한 유형을 결정할 수 있습니다. 그러나 둘 다 극도로 드물게 사용해야합니다. 이미 언급했듯이 동적 유형 식별은 디자인 결함을 나타 내기 때문에 항상 피해야합니다. (당신이 알고있는 경우 또한, 객체는 대상 유형의 확실히, 당신이 가진 내리 뜬을 할 수있다 . 이벤트 부스트 와 풀이 죽은을 할 것입니다 및 디버그 모드로, 그리고 릴리스 모드에서 단지를 사용합니다 ).A
C
A*
C*
typeid()
static_cast
polymorphic_downcast
dynamic_cast
assert
static_cast
C ++에서 개체의 유형을 절대 확인하고 싶지 않다는 데 동의하지 않습니다. 피할 수 있다면 그렇게해야한다는 데 동의합니다. 어떤 상황에서도 절대 이렇게하지 말라고 말하는 것은 너무 멀리 가고 있습니다. 이것은 매우 많은 언어로 할 수 있으며 삶을 훨씬 더 쉽게 만들 수 있습니다. 예를 들어 Howard Pinsley는 C #에 대한 그의 게시물에서 방법을 보여주었습니다.
저는 Qt 프레임 워크로 많은 작업을합니다. 일반적으로 나는 그들이 일하는 방식에 따라 내가하는 일을 모델링한다 (적어도 그들의 프레임 워크에서 작업 할 때). QObject 클래스는 모든 Qt 객체의 기본 클래스입니다. 이 클래스에는 빠른 하위 클래스 검사로 isWidgetType () 및 isWindowType () 함수가 있습니다. 그렇다면 본질적으로 비교 가능한 자신의 파생 클래스를 확인할 수없는 이유는 무엇입니까? 다음은 이러한 다른 게시물 중 일부의 QObject 스핀 오프입니다.
class MyQObject : public QObject
{
public:
MyQObject( QObject *parent = 0 ) : QObject( parent ){}
~MyQObject(){}
static bool isThisType( const QObject *qObj )
{ return ( dynamic_cast<const MyQObject*>(qObj) != NULL ); }
};
그런 다음 QObject에 대한 포인터를 전달할 때 정적 멤버 함수를 호출하여 파생 클래스를 가리키는 지 확인할 수 있습니다.
if( MyQObject::isThisType( qObjPtr ) ) qDebug() << "This is a MyQObject!";
문제를 올바르게 이해했는지 모르겠습니다. 제 말로 다시 말씀 드리겠습니다.
문제 : 주어진 클래스 B
및 D
, D
의 하위 클래스 인지 확인 B
(또는 그 반대?)
솔루션 : 템플릿 마법을 사용하십시오! 좋아, 진지하게 당신은 전설적인 C ++ 저자 인 Andrei Alexandrescu가 만든 훌륭한 템플릿 메타 프로그래밍 라이브러리 인 LOKI를 살펴볼 필요가 있습니다.
보다 구체적으로 LOKI를 다운로드 하고 TypeManip.h
소스 코드에 헤더 를 포함 시킨 후 SuperSubclass
다음과 같이 클래스 템플릿을 사용합니다.
if(SuperSubClass<B,D>::value)
{
...
}
문서에 SuperSubClass<B,D>::value
따르면가 B
의 공용 기반 D
이거나 B
및 D
동일한 유형의 별칭 이면 true 입니다.
즉 하나 D
의 하위 클래스 B
또는 D
동일하다 B
.
이게 도움이 되길 바란다.
편집하다:
의 평가 SuperSubClass<B,D>::value
는를 사용하는 일부 메서드와 달리 컴파일 타임 에 발생 dynamic_cast
하므로 런타임에이 시스템을 사용하는 데 따른 불이익이 없습니다.
#include <stdio.h>
#include <iostream.h>
class Base
{
public: virtual ~Base() {}
template<typename T>
bool isA() {
return (dynamic_cast<T*>(this) != NULL);
}
};
class D1: public Base {};
class D2: public Base {};
class D22: public D2 {};
int main(int argc,char* argv[]);
{
D1* d1 = new D1();
D2* d2 = new D2();
D22* d22 = new D22();
Base* x = d22;
if( x->isA<D22>() )
{
std::cout << "IS A D22" << std::endl;
}
if( x->isA<D2>() )
{
std::cout << "IS A D2" << std::endl;
}
if( x->isA<D1>() )
{
std::cout << "IS A D1" << std::endl;
}
if(x->isA<Base>() )
{
std::cout << "IS A Base" << std::endl;
}
}
결과:
IS A D22
IS A D2
IS A Base
나는 사용 라인을 따라 생각하고 있었다
typeid()
...
예, 다음을 비교하여 수행 할 수 있습니다 typeid().name()
.. 이미 설명한 상황을 취하면 다음과 같습니다.
class Base;
class A : public Base {...};
class B : public Base {...};
void foo(Base *p)
{
if(/* p is A */) /* do X */
else /* do Y */
}
가능한 구현 foo(Base *p)
은 다음과 같습니다.
#include <typeinfo>
void foo(Base *p)
{
if(typeid(*p) == typeid(A))
{
// the pointer is pointing to the derived class A
}
else if (typeid(*p).name() == typeid(B).name())
{
// the pointer is pointing to the derived class B
}
}
RTTI를 사용하지 않는 한 템플릿을 사용하여 컴파일 타임에만 수행 할 수 있습니다.
유형에 대한 정보를 포함하는 type_info 구조에 대한 포인터를 생성하는 typeid 함수를 사용할 수 있습니다.
Wikipedia 에서 읽기
C #에서는 간단히 다음과 같이 말할 수 있습니다.
if (myObj is Car) {
}
템플릿 (또는 SFINAE (Substitution Failure Is Not An Error))을 사용 하여 수행 할 수 있습니다 . 예:
#include <iostream>
class base
{
public:
virtual ~base() = default;
};
template <
class type,
class = decltype(
static_cast<base*>(static_cast<type*>(0))
)
>
bool check(type)
{
return true;
}
bool check(...)
{
return false;
}
class child : public base
{
public:
virtual ~child() = default;
};
class grandchild : public child {};
int main()
{
std::cout << std::boolalpha;
std::cout << "base: " << check(base()) << '\n';
std::cout << "child: " << check(child()) << '\n';
std::cout << "grandchild: " << check(grandchild()) << '\n';
std::cout << "int: " << check(int()) << '\n';
std::cout << std::flush;
}
산출:
base: true
child: true
grandchild: true
int: false
std::is_base_of
. : 3