2012-02-04 5 views
3
가 난 할 노력하고있어 것은 다소 불분명

, 그래서 (이 실제 코드가 아닙니다) 제가 예를 보여 드리겠습니다 : 기본적으로타입 캐스팅 방법 포인터

template <class T> 
class ArrayStorage { 
protected: 
    void processStuff(void (ArrayStorage<T>::*procedure)(T *)) { 
     for (int i = 0; i < count; i++) 
      (this->*procedure)(content[i]); 
    } 
    // No method of type void (ArrayStorage<T>::*)(T *) 

private: 
    T **content; 
    int count; 
}; 

class DrawableStorage : public ArrayStorage<Drawable> { 
public: 
    void drawStuff() { 
     processStuff((void (ArrayStorage<Drawable>::*)(Drawable *)) &DrawableStorage::drawOne); 
    } 

private: 
    void drawOne(Drawable *item) { 
     item->draw(); 
    } 
}; 

, 일반적인가 컨테이너를 사용하여 항목을 반복하고 각각에 대한 메소드를 사용할 수 있습니다 (포인터가 매개 변수에 있음). 그러나이 메서드는이 클래스에는 없지만 해당 하위 클래스에만 있습니다. Subclass "drawStuff"메서드에서 서브 클래스의 메서드 포인터를 제공하지만 기본 클래스의 메서드로 형식이 지정되었는지 확인할 수 있습니다.

대단히 성공적으로 컴파일되고 정상적으로 작동합니다.

내 질문은, 그건 내 우연의 일치는 내 특정 컴파일러는 실제로 그것을 완전히 처리 할 수있는 동안 내가 그것을 제거해야하거나 올바른 방법 포인터의 사용법입니다?

감사합니다.

답변

1

그것은 $ 5.2에 따라 합법적이다.9/12 :

"타입 CV1 (T)의 D의 부재 포인터 '유형의 prvalue가 될 수는 입력 CV2 (T)의"B의 멤버에 대한 포인터 "타입의 prvalue로 변환 는 B가이고 유효한 표준 인 "T 타입의 B의 멤버에 대한 포인터"에서 "T 타입의 D의 멤버 인 에 대한 포인터"로의 변환이 있고 (4.11) cv2가 동일하면 인 D의 기본 클래스 (10 절) cv-qualification은 cv1보다 크거나 cv-qualification보다 크다. 69 null 멤버 포인터 값 (4.11)이 null 멤버 포인터 대상 유형 값으로 변환됩니다. 클래스 B가 원래 멤버를 포함하거나 원래 멤버를 포함하는 클래스의 기본 클래스 또는 파생 클래스 인 경우 멤버에 대한 결과 포인터는 원래 멤버를 가리 킵니다. 그렇지 않으면 캐스트 결과가 정의되지 않습니다. [참고 : 클래스 B는 원본 멤버를 포함 할 필요가 없지만 멤버에 대한 포인터가 역 참조되는 동적 개체 유형은 원래 멤버가 포함되어야합니다 ( ). 5.5 참조. -end note]

그러나 다중 상속 또는 가상 상속으로이 작업을 수행하려고하면 모든 컴파일러가 표준 호환 방식으로 멤버 함수 포인터를 구현하지 않기 때문에 상황이 중단되기 시작합니다. MSVC 및 Intel 컴파일러에서 모든 멤버 함수 포인터의 크기가 같지 않으므로 변환에 대한 중요한 정보를 잃게됩니다.

3

이것은 포인터를 사용하여 가능한 속임수 중 하나입니다. DrawableStorage가 ArrayStorage이고 함수 헤더가 예상 한 것과 일치하기 때문에 작동합니다 (객체 템플릿 유형과 일치하는 유형에 대한 포인터를 취하여 void를 반환 함).

일단 부모 개체가 함수에 대한 포인터를 갖고 있으면 실제로 함수 자체가 구성원이 아니더라도 호출 할 수 있습니다.

처음에는 읽기가 약간 혼란 스럽지만, 함수 포인터를 완벽하게 사용하는 것은 사실입니다.

0

기본 클래스에 이 없으므로 요청한 멤버 함수가이므로이를 해결할 방법이 없습니다. 다음과 같이

template <typename S> 
void processStuff(S * s, void (S::*procedure)(T *)) 
{ 
    for (int i = 0; i < count; i++) { (s->*procedure)(content[i]); } 
} 

그것을 호출 :

#include <iostream> 
using namespace std; 

struct Arg 
{ 
    int i; 
}; 

class A; 
typedef void (A::*fncptr)(Arg&); 

fncptr은 다음과 같습니다

processStuff(this, &DrawableStorage::drawOne); 
+0

그래, 생각해 봤지만 내 버전이 가능한지 궁금해서. – Detheroc

+0

@Detheroc : 당신이 쓰는 방식이 아닙니다. 기본 클래스에 가상 인터페이스가 있다면 어쩌면이 방법이 완벽하게 사용할 수 있다면 왜 귀찮을까요? 전체 함수 호출은 아마 인라인 될 것이고 실제 코드를 발생시키지 않을 것이다. –

1

여기 예제의 간단한 버전입니다 그러나 기본 기능을 대신 템플릿을 만들 수 있습니다 참조로 Arg을 취하는 클래스 A의 멤버 함수 (메서드)에 대한 포인터. 누군가이 유형의 비 정적 함수의 주소를 제공하면 클래스 A의 객체가있는 경우에만 호출 할 수 있습니다 (그렇지 않으면 정적 함수 여야합니다).

class A 
{ 
public: 
    A(Arg a) : a(a) { } 
    void process(fncptr f){ (this->*f)(a); } 

protected: 
    Arg a; 
}; 

class B : public A 
{ 
public: 
    B(Arg a) : A(a) { } 
    void magic(){ process((fncptr)&B::function); } 
    void function(Arg& a) { cout << a.i; } 
}; 

는 여기에 트릭 : this : 당신이 클래스 A의 멤버 함수 인 기능 process에 있기 때문에 당신이 이미 하나를 가지고 있기 때문에, 당신은 기능 f를 호출하는 클래스 A의 객체에 대한 포인터를 필요가 없습니다. 따라서이 클래스 나 파생 클래스의 모든 함수를 호출 할 수 있으며 그렇게하려면 해당 함수의 주소 만 필요합니다.

여기 main()입니다 :

int main() 
{ 
    Arg a; 
    a.i = 71; 

    B* b = new B(a); 
    b->magic(); 
    delete b; 
} 

출력 : 71