2011-03-11 4 views
9

오늘 T이 어떤 범위에서 타입과 객체 일 때 을 T::bar뿐만 아니라 T.bar으로 만드는 C++의 속성을 대략 남용하는 C++ "멤버 공간"관용구에 대해 알게되었습니다."구성원 공간"관용구 사용?

struct A { 
    struct Controls { 
    /* put some typedefs/data/functions here */ 
    } Controls; 
}; 

// Can be used as a type and value 
A a; 
A::Controls::iterator it = a.Controls.begin(); 

실제로이 관용구를 사용해 본 적이 있습니까? 유용하다고 생각하니? 이디엄의 좋은 점이나 가장 좋은 점은 무엇입니까?

+3

모두! 불쾌한! 블레치! –

답변

6

아니, 그 기술을 사용한 적이 (그리고 나는 그것이 '관용구'라고 할 만하다 생각하지 않는다) :

나는 그것을 사용하지 않은 때문에

, 나는 그것이 유용하다고하지 않았습니다.

이 기술을 잘 적용하면 다른 프로그래머를 혼동시킬 수 있습니다.

또 다른 응용 프로그램은 많은 상상할 수없는 실전에 직면 한 문제, 아마도 많은 템플릿 메타 프로그래밍으로 난독 화 된 것에 대해 얼마나 멋진 지에 대한 테크놀러블 한 글을 쓸 수도 있습니다.

Dunno, 가장 좋은 응용 프로그램은 아마도 그 모든 바보 같은 규칙에 대한 기사를 작성하는 것입니다. 예를 들어 struct과 동일한 범위의 동일한 이름의 기능을 사용할 수 있습니다. 그것들이 성취 할 수있는 것은 언어의 더 어두운 구석에서 멀리 떨어져서 더 잘 수행 될 수 있습니다. :-) 기사는 돈으로 많은 돈을 지불하지 않지만 존경심으로 돈을 쓰고 쓰기가 재미 있습니다. 그것을 써주세요 (TIA).

건배 & HTH.,

+2

이에 대한 기사는 http://accu.org/index.php/journals/1527에 있습니다. 제 동료가이 관용구/"관용구"에 대해 저에게 말했습니다. –

+1

@litb : "이것은 합법적이며 명확한 목적을 제공하지는 않지만 코드에서 구성원 공간을 쉽게 인식 할 수있는 방법입니다." 클래스 이름과 객체 이름을 쉽게 구별 할 수있는 사람들은 그것을 사용할 필요가 없습니다 :-) 나는이 명명 규칙이이 기사에서 설명한 관용법에 중요하다고 생각하지 않습니다. 관용구는 그렇지 않으면 매우 합리적입니다. 간단한 예제에서는 공용 인터페이스에서 객체의 구성을 노출하는 것입니다. –

+0

아, 그리고 포함 된 객체에 대한 참조를 유지하는 방해받지 않는 기법과 비 침투적인 기법은 Java에서 내부 클래스의 기능을 재현하는 비뚤어진 방법입니다. "offsetof"의 dodgy 사용을 위해 나는 Alf가 말한 것을 향하여 의지한다. 아마도 동료가 너를 사냥해서 죽이기를 원하지 않는다면 아마도 기사 사료로 더 많이 쓰일 것이다. –

1

아니, 나는 그것을 사용한 적이 없으니까.

좋은가요? 그들은 간단한 문제에 대한 복잡한 솔루션 (memberspace 관용구는 달리, 그 템플릿을주의 shoudn't 곳 어쩌면 당신은 당신이 좀 pleople 사용 템플릿처럼 ... 그들이 더 나은 것을 동료를 표시하는 데 사용할 수 있습니다, 때로는 유용). @ 스티브 이미 코멘트에 말했듯이

+3

때때로 유용합니까? 매우 유용하고 가끔 부적절한 것은 어떨까요? – GManNickG

+1

예, 종종 매우 유용하지만, 경험이 거의없는 프로그래머가 가장 부적절하게 사용하는 C++ 기능 중 하나입니다. – fbafelipe

+0

어떻게 유용할까요? –

2

는 중첩 된 형태와 유형의 인스턴스가 같은 이름을 가지고 있다는 사실이 기술의 핵심 측면이 아니다. 캡슐화를 높이기 위해 인스턴스 함수에 액세스하기 위해 멤버 함수를 사용할 수도 있습니다 (네임 스페이스 한정자처럼 보이지는 않지만). 예를 들어, 당신이 준 예는 단점없이 다음과 같이 다시 작성할 수 있습니다 (물론, 어쩌면있다, 그러나 나는 순간에 하나를 찾을 수 없습니다) :

여기
struct A 
{ 
    struct Controls 
    { 
     //typedefs/data/functions 
    }; 

    const Controls & getControls() { return controls_; } 

    private: 

    Controls controls_; 
}; 

A a; 
A::Controls::iterator = a.getControls().begin(); 

내가 memberspaces를 참조하는 방법입니다. 구성원 공간의 목표는 관련된 typedef와 메소드를 그룹화하기 위해 클래스의 이름 공간을 나누는 것입니다. 위의 Controls 클래스는 A 외부에서 정의 할 수 있지만 A (각각 AControls과 연결되어 있고 Controls은 포함되어있는 객체의 뷰일뿐입니다))가 느끼는 "자연"이 A의 멤버로 만들려면, 등) A의 내부에 접근 할 필요가있는 경우 가능도 (A로에게 친구를합니다.

그래서 기본적으로 우리가 둘러싸는 클래스 네임 스페이스를 오염하지 않고, 단일 개체에 하나 또는 여러 개의 뷰를 정의 할 수 memberspaces. 기사에서 언급 한 바와 같이 당신이 당신의 클래스의 객체를 반복하는 여러 가지 방법을 제공하려는 경우, 이것은 매우 흥미로운 일이 될 수 있습니다.

예를 들어, C++ 클래스를 나타내는 클래스를 작성한다고 가정 해 보겠습니다. Class라고 부르 자. 클래스에는 이름과 모든 기본 클래스 목록이 있습니다. 편의상, Class를 상속받은 모든 클래스 (파생 클래스)의 목록도 저장하도록하겠습니다. 그래서 그런 코드 것이다 : 이제

class Class 
{ 
    string name_; 
    list< shared_ptr<Class> > baseClasses_; 
    list< shared_ptr<Class> > derivedClasses_; 
}; 

을, 나는 기본 클래스/파생 클래스 추가/제거하기 위해 일부 멤버 함수가 필요합니다

class Class 
{ 
    public: 

    void addBaseClass(shared_ptr<Class> base); 
    void removeBaseClass(shared_ptr<Class> base); 

    void addDerivedClass(shared_ptr<Class> derived); 
    void removeDerivedClass(shared_ptr<Class> derived); 

    private: 

    //... same as before 
}; 

그리고 때때로을 나는 방법을 추가해야 할 수도 있습니다

class Class 
{ 
    public: 

    typedef list< shared_ptr<Class> >::const_iterator const_iterator; 

    const_iterator baseClassesBegin() const; 
    const_iterator baseClassesEnd() const; 

    const_iterator derivedClassesBegin() const; 
    const_iterator derivedClassesEnd() const; 

    //...same as before 
}; 

우리가 아직 관리 할 다루고있는 이름의 양, 그러나 우리는 역 반복을 추가하려면 무엇을 : 기본 클래스와 파생 클래스를 반복 하는가? 파생 클래스를 저장할 기본 유형을 변경하면 어떻게됩니까? 그것은 또 다른 유형의 typedef를 추가 할 것이다. 게다가, 반복자를 시작하고 끝내기위한 접근 방식이 표준 명명법을 따르지 않는다는 것을 알았을 것입니다. 즉, 우리는 추가적인 노력없이 Boost.Range와 같은 일반적인 알고리즘을 사용할 수 없다는 것을 알았을 것입니다.

실제로 우리가 접두사/접미어를 사용하여 논리적으로 그룹화하는 멤버 함수 이름을 볼 때, 우리가 이제는 네임 스페이스가있는 것을 피하려고 노력하는 것은 명백합니다. 그러나 클래스에서 네임 스페이스를 사용할 수 없기 때문에 트릭을 사용해야합니다.

이제 구성원 공간을 사용하여 모든 기본 관련 및 파생 관련 정보를 자체 클래스에 캡슐화하여 관련된 데이터/작업을 함께 그룹화 할 수있을뿐만 아니라 코드 중복을 줄일 수 있습니다. 기본을 조작하기위한 코드 ,

이 예에서
Class c; 
Class::DerivedClasses::const_iterator = c.derivedClasses.begin(); 

boost::algorithm::find(c.derivedClasses, & c); 
... 

, 중첩 된 클래스가 너무 Class에 연결되지 않습니다

class Class 
{ 
    struct ClassesContainer 
    { 
     typedef list< shared_ptr<Class> > const_iterator; 

     ClassesContainer(list< shared_ptr<Class> > & classes) 
      : classes_(classes) 
     {} 

     const_iterator begin() const { return classes_.begin(); } 
     const_iterator end() const { return classes_.end(); } 

     void add(shared_ptr<Class> someClass) { classes_.push_back(someClass); } 
     void remove(shared_ptr<Class> someClass) { classes.erase(someClass); } 

     private: 

     list< shared_ptr<Class> > & classes_; 
    }; 

    public: 

    typedef ClassesContainer BaseClasses; 
    typedef ClassesContainer DerivedClasses; 

    // public member for simplicity; could be accessible through a function 
    BaseClasses baseClasses; // constructed with baseClasses_ 
    DerivedClasses derivedClasses; // constructed with derivedClasses_ 

    // ... same as before 
}; 

지금 내가 할 수있는 : 클래스와 파생 클래스는 우리가 단 한 번의 중첩 클래스를 사용할 수 있습니다, 동일 그래서 그것은 정의 될 수있다. utside, 그러나 당신은 더 강한 경계를 가진 예제를 발견 할 수 있습니다.


글쎄,이 긴 게시물 이후, 나는 정말로 당신의 질문에 대답하지 않았다는 것을 알았습니다 :). 따라서 실제로 코드에서 구성원 공간을 실제로 사용한 적이 없지만 응용 프로그램이 있다고 생각합니다.

필자는 라이브러리에 대한 Facade 클래스를 작성했을 때 한두 번 생각해 보았습니다.이 Facade는 단일 진입 점을 사용하여 라이브러리를 더 쉽게 사용할 수 있도록하기위한 것이었지만 결과적으로 여러 멤버 기능은 모두 관련되었지만 "관련성"의 정도가 다릅니다. 또한, 그것은 객체의 모음을 표현했기 때문에 "기능 지향적"멤버 함수 외에도 반복 관련 typedef와 멤버 함수가 포함되었습니다. 필자는 구성원 공간을 사용하여 클래스를 논리적 인 "부분 공간"으로 나누어보다 깨끗한 인터페이스로 간주했습니다. 왜 내가하지 않았는지 모르겠다.

+0

면책 조항 :이 답변에 쓰여진 코드는 "즉석에서"작성되었으므로 개선 될 수있는 엄청난 양의 오류, 실수 및 부품이 포함되어있을 수 있습니다. –

1

이든이든 혼동이 없든 관계없이 모든 구문의 유연성은 언제나 환영합니다. 사실 트릭이 boost.array에서 사용되는, 이는 최소한의 구현 :

사용자가 인스턴스하지 않는 특성으로 고정 멤버 함수/정적 멤버 변수/중첩 클래스보고 싶어 그렇다면
#include<cassert> 

template<unsigned N> 
struct fixed_array{ /* bla bla */ 
    static unsigned size(){ 
     return N; 
    } 
}; 


int main(){ 
    fixed_array<3> arr; 
    assert(arr.size() == 3);    //like a stl container 
    assert(fixed_array<3>::size() == 3); //more proper, but less generic wrt stl containers 
    return 0; 
} 

그/그녀가 할 수있는 수업.예를 들어 일반적인 코드를 작성하는 데 유용하며,이 예제에서는 전혀 혼동하지 않습니다.