2017-01-13 9 views
3

의 내가 좋아하는 뭔가가 있다고 가정하자슈퍼 클래스 생성자에서 호출 객체의 하위 클래스를 알 수있는 방법이 있습니까? C++

class A 
{ 
    public: 
    A(A* owner) 
    { 
     m_owner=owner; 
     if (dynamic_cast<B*>(this)) 
     { 
      m_id=sm_bid; 
      sm_bid++; 
     } 
     else 
     { 
      m_id=sm_aid; 
      sm_aid++; 
     } 
    } 
    private: 
    int m_id; 
    A* m_owner; 
    static int sm_aid; 
    static int sm_bid; 
}; 

A::sm_aid=0; 
A::sm_bid=0; 

class B: public A 
{ 
    B(A* owner) : A(owner) {} 
}; 

불행하게도, dynamic_cast는이 (인스턴스화 경우) 그것이 B 객체가 있음을 잡을 수 없습니다입니다. 이 사운드는 논리적입니다 (슈퍼 클래스의 생성자가 서브 클래스의 생성자를 호출하기 전에 호출되기 때문에 이러한 기능을 구현하는 방법이 있습니까?)

+0

'std :: type_info'를'A :: A'로 전달하는 것은 어떻습니까? – Quentin

+1

'A'의 생성자가'B's 이전에 호출 되었기 때문에 (즉,'B'가 실제로 구성되는 정보가 있기 전에), 아니요. 어쨌든 왜 이것을하고 싶습니까? - 이것은 Liskov 대체 원칙을 준수하는 것과 같은 훌륭한 기술과 완전히 반대입니다. – Peter

+3

이것은 좋은 디자인 같지 않습니다. 왜 기초 계층은 그 아이에 대해 어떤 지식을 가지고 있습니까? – freakish

답변

1

합니다.

class B; 
class C; 

class A 
{ 
public: 
    template<class Derived> 
    struct my_derived 
    { 
     using type = Derived; 
    }; 

protected: 
    explicit A(my_derived<B>) 
    { 
     // Do whatever you want when derived is B 
    } 

    explicit A(my_derived<C>) 
    { 
     // Do whatever you want when derived is C 
    } 

private: 

}; 

class B : A 
{ 
public: 
    B() : A(A::my_derived<B>()) 
    { 
    } 
}; 

class C : A 
{ 
public: 
    C() : A(A::my_derived<C>()) 
    { 
    } 
}; 

또 다른 접근법은 AMA가 제안하는 템플릿 기반 프로그래밍입니다. 그러나 템플릿은 C++의 강력한 기능이지만 자체 문제가 있습니다. 그것들은 cpp 파일로 구현 될 수 없다. 이로 인해 코드가 커져서 더 큰 코드 크기로 인해 성능에 영향을 줄 수있어 명령 캐시 미스 비율이 높아집니다.

그래서이 접근 방식은 런타임 성능에 영향을 미쳤으며 기본 클래스에 하나의 가상 함수를 선언하고 템플릿 기반 접근 방식을 선언해야한다는 것을 의미하는 RTTI보다 낫다고 생각합니다. 그것은이처럼 보인다

+0

잘 작동합니다 (파생 클래스 B 또는 C를 사용하는 데 적합합니다). 나는 Swig가 이것을 소화시키는 방법을 알아야합니다 :). 틀림없이 템플릿 접근 방식이 더 우아합니다. – vpuente

2

디자인이 복잡해 보이고 거의 기본 구현없이 구현 될 수 있습니다.

그러나 C++로 다리에서 자신을 쏘는 것은 거룩한 권리입니다. 따라서 Curiously Recurring Template Pattern (CRTP)을 사용하여 목표 (정적 다형성을 통해 생성자의 다형성 동작)를 달성 할 수 있습니다.

Here is the quick and dirty sketch

:

#include <iostream> 
#include <type_traits> 

struct Owner 
{}; 

struct B; 

template <class Derived> 
struct A : public Owner 
{ 
    A(Owner*) 
    { 
     if (std::is_same<Derived, B>::value) 
      std::cout << "called from derived B" << std::endl; 
     else 
      std::cout << "not called from derived B\n" << std::endl; 
    } 
}; 

struct B : public A<B> 
{ 
    B(Owner* owner) : A<B>(owner) {} 
}; 

int main() 
{ 
    A<void> a(nullptr); 
    B b(&a); 
} 

출력 :

not called from derived B 
called from derived B 

: 나는 당신이 계산을 위해 정적 변수를 사용하는 것으로 나타났습니다. 각 템플릿 인스턴스에는 자체 정적 변수가 있습니다.

+0

답변 해 주셔서 감사합니다. 구조체를 피하기 위해 약간의 코드를 변경했습니다. – vpuente

+0

@vpuente이 답변이나 다른 답변으로 질문을 해결 한 경우 수락을 고려해보십시오 (투표 아래의 체크 표시를 클릭). 이것은 해결책을 찾았으며 응답자와 자신 모두에게 약간의 평판을 제공함을 나타냅니다. 이를 수행 할 의무는 없습니다. – AMA

+0

완료! 다시 한번 감사합니다. – vpuente

2

첫 번째로 dynamic_cast이 작동하려면 기본 클래스에 적어도 하나의 가상 메소드가 있어야합니다. 일반적으로 다른 이유로 인해 소멸자가 가상으로 만들어집니다. A에서 A 하위 객체, 구축 할

둘째, S 클래스 정의의 생성자를 모든 가상 기능을 A을 참조 할 것 '(당신이 B의 하위 객체로 A를 구성하는 사실에있는 경우에도)과 dynamic_castthis이 아니라고 말할 것이다 a 유형 B. 객체 B 생성자의 완료 될 때까지 유형 B의 대상이 아닌, 그래서 어떤 모든 검사가 B 그것을 말할 것이다 유형이 있는지를 확인하려고하기 때문에

이 때문에 아니에요입니다 개체는 사실 B (아직) 유형이 아닙니다.

나는 이것이 당신을 위해 혼란을 없애기를 희망합니다.

는 편집 :
난 그냥 당신이 대신에 해결책을 요청 질문에서 마지막 단락을 발견했습니다.

이 문제에 대해 더 잘 알지 못하면이 기능을 달성하는 데 도움이 될 것입니다. 내 제안은 그것을하지 않는 것입니다. 아마도 다른 클래스를 사용하여 A을 시도하는 것보다 객체에 ID를 할당하는 것이 좋습니다.

정말 A에 있어야한다면 아마도이 도움이 될 수 : A 당신이 태그를 기반으로 오버로드 기술을 사용할 수있는 추상 형식이

struct A 
{ 
    template <class FinalType> 
    A(A* owner, FinalType *){...}//use std::is_same to check if `B` or not 
}; 

struct B : A 
{ 
    B(A * owner):A(owner, this){} 
}; 
0

잘 작동합니다 :

#include <iostream> 
#include <type_traits> 

class Owner 
{}; 

class B; 

template <class Derived> 
class A : public Owner 
{ 
    public: 
    A(Owner* owner) 
    { 
     m_owner=owner; 
     if (std::is_same<Derived, B>::value) 
      std::cout << "called from derived B" << std::endl; 
     else 
      std::cout << "not called from derived B\n" << std::endl; 
    } 
    private: 
    Owner* m_owner; 
}; 

class B : public A<B> 
{ 
    public: 
    B(Owner* owner) : A<B>(owner) 
    {} 
}; 

int main() 
{ 
    A<void> a(nullptr); 
    B b(&a); 
} 

명예를 AMA에. 뭐든지 C++에서 수행 할 수 있습니다