2012-05-22 2 views
3

나는 느슨하게이 solution을 기반으로하는 n 차원 카티 전 곱 클래스 집합에서 작업하고 있습니다.첫 번째 반복에서 C++ 카티 전 곱 반복자를 호출하는 기본 클래스 함수

동일한 기본 알고리즘 세트에 대해 여러 가지 데이터 유형이 있으며 "아하! 템플릿을 사용하여 전반적인 작업을 줄입니다!"라고 생각했습니다. 그리고, 지금까지, 그것은 굉장히 일하고있었습니다. Boost의 iterator_facade를 사용하고 있습니다.

내 문제는 내가 map<char, boost::integer_range<int> >과 함께 작업 한 파생 클래스입니다. 반복 할 때마다 map<char,int>이 나오지만 두 번째 값이 0 인 쌍은 내 알고리즘과 관련하여 공간을 낭비하기 때문에 제외됩니다.

이 파생 클래스는 반복기의 반환 값을 생성하는 함수를 오버로드하며 작동합니다. 그러나 첫 번째 반복 동안 기본 클래스의 생성기가 호출됩니다. 나는 많이 혼란 스럽다. 누구든지 아이디어가 있습니까?


#include <boost/container/flat_map.hpp> 
#include <boost/tuple/tuple.hpp> 
#include <boost/iterator/iterator_facade.hpp> 
#include <boost/range/irange.hpp> 
#include <utility> 
#include <iostream> 

using namespace boost; 
using namespace boost::tuples; 
using namespace std; 

template <class Container2DMap, 
    class return_type = boost::container::flat_map<typename Container2DMap::value_type::first_type, 
    typename Container2DMap::value_type::second_type::value_type> > 
class CartProductIterator2DMap : public boost::iterator_facade< 
CartProductIterator2DMap<Container2DMap, return_type>, 
const return_type, 
boost::forward_traversal_tag> { 
public: 
    typedef typename Container2DMap::value_type::first_type first_type; 
    typedef typename Container2DMap::const_iterator first_iterator; 
    typedef typename Container2DMap::value_type::second_type::value_type second_type; 
    typedef typename Container2DMap::value_type::second_type::const_iterator second_iterator; 

    CartProductIterator2DMap(const Container2DMap &container) { 
     rangeIterSetup(container); 
    } 

    CartProductIterator2DMap() : _finished(true) {} 
    virtual ~CartProductIterator2DMap() {} 
private: 
    virtual bool equal(const CartProductIterator2DMap &other) const { 
     if (_finished || other._finished) { 
      if (_finished && other._finished) { 
       return true; 
      } else { 
       return false; 
      } 
     } else if (_currentIter == other._currentIter) { 
      return true; 
     } else { 
      return false; 
     } 
    } 
    virtual void increment() { advance(); } 
    virtual void advance() { 
     advanceIter(); 
    } 

    virtual const return_type& dereference() const { return _currentIter; } 

protected: 
    struct mode { 
     const static bool stopIter = false; 
     const static bool continueIter = true; 
    }; 
    typedef boost::tuple<second_iterator, 
      second_iterator, 
      second_iterator> SecondIterDescription; 
    typedef boost::container::flat_map<first_type, SecondIterDescription> RangeIterMap; 
    friend class boost::iterator_core_access; 
    return_type _currentIter; 
    RangeIterMap _rangeIter; 
    bool _finished; 
    bool _iterMode; 

    virtual void advanceIter() { 
     if (_iterMode == mode::continueIter) { 
      _currentIter = genReturnValue(_rangeIter); 
      _iterMode = advanceRangeIter(_rangeIter); 
     } else { 
      _finished = true; 
     } 
    } 
    virtual void rangeIterSetup(const Container2DMap &container) { 
     _finished = false; 
     if (container.empty()) { 
      _iterMode = mode::stopIter; 
     } else { 
      _iterMode = mode::continueIter; 
      for (typename Container2DMap::const_iterator it = container.begin(); 
        it != container.end(); ++it) { 
       _rangeIter.insert(
         make_pair(it->first, 
           SecondIterDescription(it->second.begin(), it->second.end(), it->second.begin()) 
           ) 
           ); 
      } 
      advance(); 
     } 
    } 


    virtual return_type genReturnValue(const RangeIterMap &rangeIter) { 
     std::cout << "Calling base class." << std::endl; 
     return_type returnValue; 
      for(typename RangeIterMap::const_iterator it = rangeIter.begin(); 
        it != rangeIter.end(); ++it) { 
       returnValue.insert(
         make_pair(it->first, *get<2>(it->second)) 
         ); 
      } 
     return returnValue; 
    } 

    virtual bool advanceRangeIter(RangeIterMap &rangeIter) { 
     for (typename RangeIterMap::iterator it = rangeIter.begin(); ;) { 
      ++(get<2>(it->second)); 
      if (get<2>(it->second) == get<1>(it->second)) { 
       if (it + 1 == rangeIter.end()) { 
        return mode::stopIter; 
       } else { 
        // cascade 
        get<2>(it->second) = get<0>(it->second); 
        ++it; 
       } 

      } else { 
       // normal break point 
       return mode::continueIter; 
      } 
     } 
     return mode::continueIter; 
    } 
}; 

typedef boost::integer_range<int> _intRange; 
typedef boost::container::flat_map<char, _intRange> CharRange; 
typedef boost::container::flat_map<char, int> ResidueCount; 

template <class Container2D, 
    class return_type = boost::container::flat_map<typename Container2D::value_type::first_type, 
    typename Container2D::value_type::second_type::value_type> > 
struct BaseIterContainer { 
    typedef CartProductIterator2DMap<Container2D, return_type> const_iterator; 
    const Container2D &_container; 
    BaseIterContainer(const Container2D &container) : _container(container) {} 
    const_iterator begin() const { return const_iterator(_container); } 
    const_iterator end() const { return const_iterator(); } 
}; 

typedef BaseIterContainer<CharRange, ResidueCount> BaseCharRangeIter; 

typedef CartProductIterator2DMap<CharRange, ResidueCount> BaseCPIterator; 
class DerivedCPIterator : public BaseCPIterator { 
public: 
    DerivedCPIterator() : BaseCPIterator() {} 
    DerivedCPIterator(const CharRange & charRange) : BaseCPIterator(charRange) {} 
protected: 
    ResidueCount genReturnValue(const RangeIterMap &rangeIter) { 
     std::cout << "Calling derived class." << std::endl; 
     ResidueCount returnValue; 
      for(RangeIterMap::const_iterator it = rangeIter.begin(); 
        it != rangeIter.end(); ++it) { 
        const char aa = it->first; 
        const int aaCount = *get<2>(it->second); 
        if (aaCount > 0) { 
         returnValue.insert(
           make_pair(aa, aaCount) 
         ); 
        } 
      } 
     return returnValue; 
    } 
}; 

struct DerivedCharRangeIter { 
    typedef DerivedCPIterator const_iterator; 
    const CharRange &_container; 
    DerivedCharRangeIter(const CharRange &container) : _container(container) {} 
    const_iterator begin() const { return const_iterator(_container); } 
    const_iterator end() const { return const_iterator(); } 
}; 

std::ostream& operator<<(std::ostream& out, const ResidueCount &rCount) { 
    foreach(const ResidueCount::value_type& aaCount, rCount) { 
     char aa = aaCount.first; 
     int totalAACount = aaCount.second; 
     out << "(" << aa << "," << totalAACount << ")"; 
    } 
    return out; 
} 



int main(int argc, char **argv) { 
    cout << "Base Container" << endl; 
    CharRange test; 
    test.insert(make_pair('a', _intRange(0, 3))); 
    test.insert(make_pair('b', _intRange(0, 3))); 
    BaseCharRangeIter t(test); 
    BaseCharRangeIter::const_iterator it = t.begin(); 
    for(;it != t.end(); ++it) { 
     cout << *it << endl; 
    } 
    cout << endl; 
    cout << "Derived Container: " << endl; 
    DerivedCharRangeIter r(test); 
    DerivedCharRangeIter::const_iterator rt = r.begin(); 
    for(; rt != r.end(); ++rt) { 
     cout << *rt << endl; 
    } 
    return 0; 
} 

내가 얻는 결과 :


Base Container 
Calling base class. 
(a,0)(b,0) 
Calling base class. 
(a,1)(b,0) 
Calling base class. 
(a,2)(b,0) 
Calling base class. 
(a,0)(b,1) 
Calling base class. 
(a,1)(b,1) 
Calling base class. 
(a,2)(b,1) 
Calling base class. 
(a,0)(b,2) 
Calling base class. 
(a,1)(b,2) 
Calling base class. 
(a,2)(b,2) 

Derived Container: 
Calling base class. 
(a,0)(b,0) 
Calling derived class. 
(a,1) 
Calling derived class. 
(a,2) 
Calling derived class. 
(b,1) 
Calling derived class. 
(a,1)(b,1) 
Calling derived class. 
(a,2)(b,1) 
Calling derived class. 
(b,2) 
Calling derived class. 
(a,1)(b,2) 
Calling derived class. 
(a,2)(b,2) 

각 genReturnValue 홍보 여기

는 관련 코드 조각입니다 int 그 클래스 (기본 또는 파생) 모든 호출. 기본 클래스는 필요한대로 작동합니다. 그러나 파생 클래스는 그렇지 않습니다. 첫 번째 반복은 기본 클래스 genReturnValue를 호출하고 0은 필터링되지 않습니다. 그러나 더 많은 반복이 있습니다.

이것은 템플릿과 파생 클래스에 대한 첫 번째 진출이기 때문에 분명히 뭔가 빠져있는 것이 틀림 없습니다.하지만 내 인생에서 나는 그것을 알 수 없습니다. GCC 4.6.3과 Clang 3.0-6은 동일한 출력을 제공합니다.

하프!

편집 : 또한 저는 C++ 프로그래밍이 비교적 새롭습니다. 나는 비평, 스타일 또는 다른 것에 대해 개방적입니다. 감사!

답변

5

실제로 포인터와 관련이 없습니다. 대신에 virtual 기능의 제한 사항입니다.

파생 된 가상 호출은 파생 된 부분과 관련된 기본 클래스의 생성 및 소멸 순서로 인해 가상으로 만 수행 될 수 없으므로 대신 정적으로 처리됩니다.

즉, Base의 생성자 또는 소멸자에서 virtualfunc에 대한 호출은 (어쨌든) Base::virtualfunc으로 해석됩니다. 이것이 순수 가상 함수라면 정의되지 않은 동작이 계속됩니다.

따라서, 일반적인 지침은 다음과 같습니다 생성자 또는 소멸자에서

  • 결코 호출 가상 함수.

예 : 예상대로

struct Base { 
    Base() { call(); } 

    virtual call() { std::cout << "Base\n"; } 
}; 

struct Derived: Base { 
    Derived(int i): _i(i); 

    virtual call() { std::cout << "Derived" << _i << "\n"; } 

    int _i; 
}; 

int main() { 
    Derived d(1); 
}; 

이, Base하지 Derived1를 인쇄합니다; 이에 대한 좋은 이유가있다 :

  • Derived()이 그것은 Derived()의 몸에 진입 그것은 자동으로 _i(i)
  • 를 호출 Base()
  • 를 호출
  • 라고, 수행 : 사물의 시공 순서를 기억 없음

그러므로 Base()이 호출되면 _i이 초기화되지 않았습니다. 그러나 call에 대한 호출은 어리석은 일입니다. 고맙게도 스탠다드는 대부분의 경우에도 처음부터 기대했던 것과는 달리 좋은 해결책을 제공 할 수있는 적합성을 보았습니다.

CartProductIterator2DMap(const Container2DMap &container) { 
    rangeIterSetup(container); 
} 

이 가상 함수가 결국 다른 가상 함수 호출 : genReturnValue

+0

와우, 우수. 너무 감사합니다. 그 이유는 근본적인 전략을 알고 있으므로 이제는 완벽하게 이해할 수 있습니다. 답변 할 시간을내어 주셔서 감사합니다. – notwithoutend

3

당신이있는 거 기본 클래스 CartProductIterator2DMap 가상 함수를 호출하는 생성자가 있습니다.

이제 기본 클래스 생성자가 실행될 때 파생 클래스가 아직 구성되지 않았습니다. 따라서 genReturnValue을 기본 클래스 생성자에서 호출하면 CartProductIterator2DMap::genReturnValue이 호출됩니다. 그러나 후속 호출 (객체가 완전히 생성 된 후)은 DerivedCPIterator::genReturnValue을 호출합니다.

기본 클래스 생성자가 가상 ​​함수를 호출하지 않아야합니다.

+0

고맙습니다. 모든 것이 완벽하게 작동합니다. – notwithoutend