2008-09-11 4 views
3

일부 레거시 C++ 코드를 리팩터링하는 동안 동일한 서명을 공유하는 모든 클래스 메서드를 가리킬 수있는 변수를 어떻게 든 정의하여 일부 코드 중복을 제거 할 수 있음을 발견했습니다. - 는이 가야 할 올바른 방법입니다C++ 클래스 메서드에 대한 포인터

class MyClass 
{ 
protected: 
    bool CaseMethod1(int abc, const std::string& str) 
    { 
     cout << "case 1:" << str; 
     return true; 
    } 

    bool CaseMethod2(int abc, const std::string& str) 
    { 
     cout << "case 2:" << str; 
     return true; 
    } 

    bool CaseMethod3(int abc, const std::string& str) 
    { 
     cout << "case 3:" << str; 
     return true; 
    } 

public: 
    bool TestSwitch(int num) 
    { 
     bool (MyClass::*CaseMethod)(int, const std::string&); 

     switch (num) 
     { 
      case 1: CaseMethod = &MyClass::CaseMethod1; 
        break; 
      case 2: CaseMethod = &MyClass::CaseMethod2; 
        break; 
      case 3: CaseMethod = &MyClass::CaseMethod3; 
        break; 
     } 

     ... 

     bool res = CaseMethod(999, "hello world"); 

     ... 

     reurn res; 
    } 
}; 

내 질문은 : 조금 파고 후, 나는 다음과 같이 뭔가를 할 수 있다는 것을 발견? 무엇이든을 고려해야합니까 부스트가 제공 하는가?

편집 ...

좋아, 내 실수 - 지금처럼 내가 호출하는 메소드 :

bool res = ((*this).*CaseMethod)(999, "Hello World"); 

답변

8

멤버 함수에 대한 포인터가 있습니다. 그것은 당신의 문제를 해결할 것입니다. 나는 당신의 "TestSwitch"함수가 컴파일되는 것에 놀랐다. 왜냐하면 호출 구문이 당신이 기대하는 것과 약간 다르기 때문이다. 그것은해야한다 :

bool res = (this->*CaseMethod)(999, "hello world"); 

그러나 부스트 : : 기능의 조합을 찾아 높일 수 :: 당신이 기괴한 호출 구문을 피할 수있는 바인드는 상황이 조금 쉬워집니다. 물론

boost::function<bool(int,std::string)> f= 
    boost::bind(&MyClass::CaseMethod1,this,_1,_2); 

이 현재 this 포인터에 바인딩됩니다

boost::function<bool(MyClass*,int,std::string)> f= 
    boost::bind(&MyClass::CaseMethod1,_1,_2,_3); 

또 다른 대안가있을 수 있습니다 : 만약 당신이 좋아하면 당신이 멤버 함수를 명시 적으로 세 번째 매개 변수의 this 포인터를 만들 수 있습니다 가상 함수와 파생 클래스를 사용하지만 코드를 크게 변경해야 할 수도 있습니다.

1

CaseMethod 호출이 올바르지 않습니다하지만 당신은 그것을 확실하게 할 수 있습니다 (그것은이다 멤버 함수에 대한 포인터이므로 메서드를 호출해야하는 객체를 지정해야합니다. 올바른 호출과 같을 것이다 :

bool res = this->*CaseMethod(999, "hello world"); 

을 다른 한편으로는, 나는 boost::mem_fn을 권하고 싶습니다 - 당신이 그것을 망치 적은 기회를해야합니다. ;)

0

여기에 제공된 지역화 된 예제에는 본질적으로 잘못된 것은 없지만 클래스 메서드 포인터는 포인터가 아닌 클래스와 같이 넓은 문맥에서 사용할 경우 '안전함'을 유지하는 것이 까다로울 수 있습니다 복잡한 상속 트리의 또는 함께. 컴파일러가 일반적으로 메소드 포인터를 관리하는 방식은 '일반적인'포인터와 다릅니다 (코드 엔트리 포인트 이상으로 추가 정보가 있기 때문에). 따라서 여러분이 할 수있는 것에 많은 제약이 있습니다.

단순한 포인터를 그대로 쓰면 괜찮을 것이지만 좀 더 복잡한 용도는 boost::bind과 같이 좀 더 일반화 된 펑터 시스템을 살펴볼 수 있습니다. 이것들은 호출 가능한 코드 포인터에 대한 포인터를 취할 수 있으며 필요한 경우 인스턴스화 된 함수 인수를 바인드 할 수도 있습니다.

1

귀하의 전화와 단순히 switch 문 내에서 메소드를 호출하는 것과 다른 점은 없습니다.

아니요, 의미 또는 가독성의 차이는 없습니다.

유일한 차이점은 메서드에 대한 포인터를 가져 와서 컴파일러가 인라인을하도록하거나 해당 메서드에 대한 호출을 최적화한다는 것입니다.

3

당신은 서면으로 끝날 수 있도록 (키 범위가 합리적인 경우) 당신은 또한 조회를 만들 수있다 :뿐만 아니라

this->*Methods[num](999, "hello world"); 

이 스위치를 제거, 그리고 좀 더 가치있는 정리한다.

1

더 넓은 맥락없이, 정답을 파악하기 힘들지만, 내가 여기에 세 가지 가능성 바느질 : 일반 스위치 문을

  • 숙박, 필요가 아무것도 할 수 없습니다. @ 사이먼 말한다하거나지도 할 수 있기 때문에 이는 가능성이 용액을 배열과 함께 멤버 함수

  • 포인터를 사용한다. 많은 사례가있는 사례 명세서의 경우 더 빠를 수 있습니다.

  • 클래스를 여러 클래스로 나눠서 각각 하나의 함수를 호출하고 가상 함수를 사용합니다. 이것은 아마도 가장 좋은 해결책입니다. 구매하려면 심각한 리퍼 레이터가 필요할 것입니다. 주 또는 방문자 또는 일부와 같은 GoF 패턴을 고려하십시오.

0

추상 기본 클래스 또는 특수 템플릿 함수 사용과 같은 다른 접근 방법이 있습니다.

기본 클래스 아이디어를 설명합니다.

당신은 어떤 시점에서

class Base { virtual bool Method(int i, const string& s) = 0; }; 

그런 다음 서브 클래스로 각각의 경우에 쓰기 추상 기본 클래스와 같은

class Case1 : public Base { virtual bool Method(..) { /* implement */; } }; 

를 정의 할 수 있습니다, 당신은 당신의 "NUM"변수를 가져올 것이다 실행할 테스트를 나타냅니다. 이 num을 취하는 factory 함수를 작성하고 (which_case라고 부름) Base에 대한 포인터를 리턴 한 다음 해당 포인터에서 Method를 호출 할 수 있습니다. 그런데

Base* CreateBase(int which_num) { /* metacode: return new Case[which_num]; */ } 
// ... later, when you want to actually call your method ... 
Base* base = CreateBase(23); 
base->Method(999, "hello world!"); 
delete base; // Or use a scoped pointer. 

,이 응용 프로그램은 나에게 정적 가상 함수, 또는 내장 유형으로 "유형"같은 것을 지원하는 C++을 기원합니다 -하지만 그렇지 않습니다.

관련 문제