3

我正在研究一组 n 维笛卡尔产品类,松散地基于这个解决方案

对于相同的基本算法集,我有许多不同的数据类型,我想“啊哈!我将使用模板来减少我的整体工作!” 而且,直到现在,它的工作都很棒。我正在使用 Boost 的 iterator_facade。

我的问题是我使用的派生类map<char, boost::integer_range<int> >。每次迭代都会产生 a 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 每次调用都会打印其类(基类或派生类)。基类发挥应有的作用。但是,派生类没有。第一次迭代调用基类 genReturnValue 并且没有过滤掉 0。然而,进一步的迭代确实如此。

由于这是我第一次涉足模板和派生类,我确信我遗漏了一些明显的东西,但对于我的生活,我无法弄清楚。GCC 4.6.3 和 Clang 3.0-6 提供相同的输出。

哈!

编辑:另外,我在 C++ 编程方面相对较新。我愿意接受批评,风格或其他,如果你有的话。谢谢!

4

2 回答 2

5

实际上,它与指针无关。但是,相反,是virtual功能的限制。

在构造和销毁过程中,由于基类相对于派生对象的构造和销毁顺序,虚拟调用不能是纯虚拟的,因此它们是静态解析的。

也就是说,virtualfunc在构造函数或析构函数中的调用Base被(以某种方式)解析为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()叫做
  • 它会自动调用Base()
  • 它调用_i(i)
  • 它进入 的 体内Derived(),它什么也不做

因此,当Base()被调用时,_i尚未初始化,因此调用call将是愚蠢的。值得庆幸的是,该标准在这里提供了一个很好的解决方法,即使它在大多数情况下并不是您最初期望的。

于 2012-05-22T17:23:22.727 回答
3

你的基类CartProductIterator2DMap有一个调用虚函数的构造函数:

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

这个虚函数最终会调用另一个虚函数:genReturnValue.

现在,当基类构造函数执行时,派生类还没有被构造。所以genReturnValue从基类构造函数调用调用CartProductIterator2DMap::genReturnValue. 但是后续调用(在对象完全构造之后)调用:DerivedCPIterator::genReturnValue

您需要确保您的基类构造函数不调用任何虚函数。

于 2012-05-22T17:25:07.047 回答