从std :: back_insert_iterator派生?

时间:2010-08-15 18:27:43

标签: c++ stl iterator std

我想从std::back_insert_iterator派生出来为字符串类型创建一种过滤器,比如back_xml_insert_iterator,它将检查通过它传递的字符,寻找无法“裸”发出的字符到XML流,例如'"''&''<''>''\'',并将即时插入其字符实体引用相反,例如,"&#34;"'"'

template<class StringType>
class back_xml_insert_iterator : public std::back_insert_iterator<StringType> {
  typedef std::back_insert_iterator<StringType> base_type;
public:
  typedef typename base_type::container_type container_type;
  typedef typename StringType::value_type value_type;

  explicit back_xml_insert_iterator( StringType &s ) : base_type( s ) { }

  back_xml_insert_iterator& operator=( value_type c ) {
    switch ( c ) {
      case '"':
      case '&':
      case '\'':
      case '<':
      case '>':
        char buf[10];
        this->container->append( "&#" );
        this->container->append( itoa( c, buf ) );
        this->container->push_back( ';' );
        break;
      default:
        this->container->push_back( c );
    }
    return *this;
  }
};

编译好。当我创建一个实例时,我确认调用了构造函数,但我的operator=()从未被调用过。我认为这是因为继承的operator*()返回back_insert_iterator&而不是back_xml_insert_iterator&,因此调用back_insert_iterator::operator=()而不是我的(因为operator=()不是,也不是是,virtual)。

如果是这种情况,那么似乎无法以有用的方式从back_insert_iterator推导出来。

如果我改为创建自己的back_insert_iterator_base类:

template<class ContainerType,class DerivedType>
class back_insert_iterator_base :
  public std::iterator<std::output_iterator_tag,void,void,void,void> {
public:
  typedef ContainerType container_type;

  DerivedType& operator*() {
    return *static_cast<DerivedType*>( this );
  } 

  DerivedType& operator++() {
    return *static_cast<DerivedType*>( this );
  }

  DerivedType& operator++(int) {
    return *static_cast<DerivedType*>( this );
  }

protected:
  back_insert_iterator_base( ContainerType &c ) : container( &c ) {
  }

  ContainerType *container;
};

并从中衍生而来:

template<class StringType>
class back_xml_insert_iterator :
  public back_insert_iterator_base< StringType, back_xml_insert_iterator<StringType> > {
  // ... as before ...
};

然后back_xml_insert_iterator按预期工作。那么是否可以从std::back_insert_iterator派生出来并使其按预期工作?

更新

以下是我要使用back_xml_insert_iterator的方式。首先,会有辅助函数:

template<class StringType> inline
back_xml_insert_iterator<StringType> back_xml_inserter( StringType &s ) {
  return back_xml_insert_iterator<StringType>( s );
}

然后编写to_xml()函数将是微不足道的:

template<class InputStringType,class OutputStringType> inline
void to_xml( InputStringType const &in, OutputStringType *out ) {
  std::copy( in.begin(), in.end(), back_xml_inserter( *out ) );
}

3 个答案:

答案 0 :(得分:2)

您不应该尝试从未设计为继承的类继承。通常,您不应继承标准容器或迭代器。请注意,在大多数情况下,迭代器的使用是在首先不需要类型层次结构的模板中。

通常最好通过提供仿函数或者实现自己的迭代器来外部添加功能 - 这不应该非常复杂。如果你真的想要实现自己的迭代器,你可以看看boost中的迭代器适配器库。

答案 1 :(得分:0)

您可以从back_insert_iterator派生,但您必须覆盖运算符才能返回派生类型。

这会使策略毫无意义吗?这取决于您的编码风格。覆盖将简单地调用基本方法并返回this,因此您最终会得到一系列复制粘贴代码,就像您在back_insert_iterator_base中所做的那样。

template<class StringType>
class back_xml_insert_iterator : public std::back_insert_iterator<StringType> {
  typedef std::back_insert_iterator<StringType> base_type;
public:
  typedef typename base_type::container_type container_type;
  typedef typename StringType::value_type value_type;

    // add trivial overrides to avoid returning base type:
  back_xml_insert_iterator &operator++() { base::operator++(); return this; }
  back_xml_insert_iterator operator++(int) {
      back_xml_insert_iterator ret( *this );
      base::operator++();
      return ret;
  }

  explicit back_xml_insert_iterator( StringType &s ) : base_type( s ) { }

  ...

当然,类似的代码块可以实现向成员 back_insert_iterator传递操作符调用,这将消除其他人推荐的继承。这可能是一种更好的方法。

请注意,使用: private std::back_insert_iterator的私有继承会通过阻止用户尝试将您的类视为不可用的内容来增加安全性。

答案 2 :(得分:0)

我可以通过调用operator = success来实现此功能。我做对了吗?

// back_insert_iterator示例

#include <iostream>
#include <iterator>
#include <vector>
using namespace std;

template<class StringType> 
class back_xml_insert_iterator : public std::back_insert_iterator<StringType> { 
   typedef std::back_insert_iterator<StringType> base_type; 
public: 
   typedef typename base_type::container_type container_type; 
   typedef typename StringType::value_type value_type; 

   explicit back_xml_insert_iterator( StringType &s ) : base_type( s ) { } 

   back_xml_insert_iterator& operator=( value_type c ) { 
      switch ( c ) { 
      case '"': 
      case '&': 
      case '\'': 
      case '<': 
      case '>': 
         break; 
      default: 
         this->container->push_back( c ); 
      } 
      return *this; 
   } 
}; 

int main () {
   vector<int> firstvector, secondvector;
   for (int i=1; i<=5; i++)
   { firstvector.push_back(i); secondvector.push_back(i*10); }

   back_xml_insert_iterator< vector<int> > back_it (firstvector);

   back_it = 2;   // It does call operator=

   copy (secondvector.begin(),secondvector.end(),back_it);

   vector<int>::iterator it;
   for ( it = firstvector.begin(); it!= firstvector.end(); ++it )
      cout << *it << " ";
   cout << endl;

   return 0;
}