2016-09-21 4 views
3

혼동? 나 너무 ... 는 다음생성자 이니셜 라이저 목록의 구성원이 아닌 초기화

typedef std::map<std::string , double> Thresholds; 

class Foo 
{ 
    public: 
     Foo(const double & _toxicThres , const double & _zeroThres) 
    : thresholds 
    (
     MapInitializer<std::string , double>() 
      .Add("toxic" , _toxicThres) 
      .Add("zero" , _zeroThres) 
    ) 

    private: 
     Thresholds thresholds; 
}; 

는 위의 잘 작동하고 생성자의 멤버 초기화 목록에서 std::map를 초기화 고려하십시오. 이제이 고려 :

typedef std::map<std::string , double> Thresholds; 
struct CommonData 
{ 
    Thresholds thresholds; 
}; 

class Foo //a mixin 
{ 
    public: 
     Foo(Thresholds & thresholds , const double & _toxicThres , const double & _zeroThres) 
    : thresholds 
    (
     MapInitializer<std::string , double>() 
      .Add("toxic" , _toxicThres) 
      .Add("zero" , _zeroThres) 
    ) 
}; 

class Bar //another mixin 
{ 
    public: 
     Bar(Thresholds & thresholds , const double & _warningThres , const double & _zeroThres) 
    : thresholds 
    (
     MapInitializer<std::string , double>() 
      .Add("warning" , _warningThres) 
      .Add("zero" , _zeroThres) 
    ) 
}; 

class OtherGasThreshold{/*...*/}; //yet another mixin, etc... 

template<typename ThresholdMixin> //Foo , Bar , or others ... 
class ThresholdSensor : public ThresholdMixin 
{ 
    public: 
     ThresholdSensor(double val1 , double val2) 
      : ThresholdMixin(cd.thresholds, val1 , val2) 
     {} 

    private: 
     CommonData cd; 
}; 

주를 MapIniializer 코드가 here에서 유래하고, 위의 컴파일되지 것입니다 물론

template<class K, class V> 
class MapInitializer 
{ 
    std::map<K,V> m; 
public: 
    operator std::map<K,V>() const 
    { 
     return m; 
    } 

    MapInitializer& Add(const K& k, const V& v) 
    { 
     m[ k ] = v; 
     return *this; 
    } 
}; 

이지만, 하나의 ThresholdSensor::CommonData에서지도를 초기화하기 위해 어떤 방법이 있음 생성자 init 동안 믹스의 즉,지도를 참조하여 전달할 수 있으며, 믹스 인 생성자에서 초기화 할 수 있습니까?

+0

참 true 고정 – nass

+0

이 코드에서는'MapInitializer'가 완전히 불필요하다는 것을 알고 있습니까? 'initializer_list' 만 사용하십시오 –

+0

@MooingDuck 더 나은, 브레이싱 된 이니셜 라이저 사용 –

답변

1

, thresholds는 생성자에 매개 변수로 전달됩니다합니다.

이니셜 라이저 구문은 수퍼 클래스와 클래스 멤버를 초기화하기위한 것입니다. 다른 모든 경우, 그 생성자의 몸이 무엇 :

class Bar 
{ 
    public: 
     Bar(Thresholds & thresholds, 
      const double & _warningThres, 
      const double & _zeroThres) 
    { 
     thresholds=MapInitializer<std::string , double>() 
      .Add("warning" , _warningThres) 
      .Add("zero" , _zeroThres) 
    } 
}; 

이 예에는 슈퍼 클래스 또는 다른 클래스 멤버가 없습니다, 그래서 이것은 잘 작동합니다. 귀하의 예제에서 생성자의 초기화 섹션에서 thresholds을 초기화해야하는 명시적인 종속성이 표시되지 않습니다.

그러나 있다고 가정 해보십시오. 어떤 종류의 종속성이 thresholds과 수퍼 클래스 또는 클래스 멤버 사이에 있다고 가정하고 지정되지 않은 종속성으로 인해 다른 개체를 초기화하기 전에 먼저 thresholds을 초기화해야합니다. 클래스 멤버 또는 수퍼 클래스는 생성자의 초기화 섹션에서 초기화해야하므로 thresholds을 초기화해야합니다.

class Bar 
{ 
    public: 
     Bar(Thresholds & thresholds, 
      const double & _warningThres, 
      const double & _zeroThres); 

    class private_bar { 
    public: 
     private_bar(Thresholds &thresholds); 
    }; 

private: 
    private_bar secret; 
}; 

이의이 Bar 생성자의 생성자 secretthresholds를 초기화 한 후, private_bar에 전달됩니다 구축 할 필요가있다 '고 가정 해 봅시다 : 우리는 구체적인 예를 사용하는 경우

그것은 훨씬 쉽다. 이것이 일어나는 상황을 상상하기 란 어렵지 않습니다. private_bar의 생성자는 초기화 된 thresholds을 사용합니다. 이제 Bar의 생성자 초기화 섹션에서 secret 멤버를 초기화했지만이 경우에는 thresholds을 초기화해야합니다. 나는 이것이 당신의 질문이 귀결되는 것이라고 믿습니다.

솔루션은 일반적으로 다음과 같은 일반적인 디자인 패턴을 취이 상황에서

:

class Bar 
{ 

     static Thresholds &init_thresholds(Thresholds &thresholds) 
     { 
      thresholds=MapInitializer<std::string , double>() 
      .Add("warning" , _warningThres) 
      .Add("zero" , _zeroThres) 

      return thresholds; 
     } 

    public: 
     Bar(Thresholds & thresholds, 
      const double & _warningThres, 
      const double & _zeroThres) 
      : secret(init_thresholds(thresholds)) 
     { 
     } 

    class private_bar { 
    public: 
     private_bar(Thresholds &thresholds); 
    }; 

private: 
    private_bar secret; 
}; 

그리고는 귀하의 질문에 대답하기 위해, "클래스의 생성자 목록에 비회원 초기화를 할 수 있습니다"의 방법입니다. 실제로 그렇게해야하는 경우에는 먼저 무언가 을 생성자 목록에서 초기화해야하기 때문입니다. 그렇지 않으면 단순히 기본 생성자 본문에서 초기화 할 수 있습니다.

그러나 생성자 목록에서 다른 것을 초기화해야하는 경우 헬퍼 함수를 ​​사용하여 비 멤버 객체를 초기화하기 위해 해당 구성 위에 "피기 백"을 추가하십시오.

2

기본 하위 개체는 데이터 멤버보다 먼저 생성되므로 일반적으로 기본 이니셜 라이저는 파생 데이터 멤버를 사용할 수 없습니다.

난 그냥 간단한 유지하고 정상적인 멤버 함수 것 :

struct Foo 
{ 
    void Init(Thresholds & thresholds) 
    { 
     thresholds.emplace("foo", 1.0); 
     thresholds.emplace("bar", 1.5); 
    } 

    // ... 
}; 

template <typename Mx> 
struct Thing : Mx 
{ 
    Thresholds thresholds; 

    Thing() { Mx::Init(thresholds); } 
    //  ^^^^^^^^^^^^^^^^^^^^^ 

    // ... 
}; 

사용법 : 문제의 생성자에서

Thing<Foo> x; 
+1

또는 믹스 인을 Initializer에 입력 한 다음 정상적으로 멤버를 구성하십시오. http : //coliru.stacked-crooked.com/a/0f3ae4d6d50adf18 (+ 복사를 피하기 위해 구조를 이동하십시오) –

+1

@MooingDuck : 나쁜 속임수는 아니지만, 나는 그런 코드를 장려 할 것이라고 확신하지는 못합니다 .-) –

+1

@MooingDuck :이 접근법은 'Thing() : Thing ({}) {}'과 개인 Thing (Thresholds t) : 임계 값 (static_cast (Mx :: Init), t)) {}' –

관련 문제