代码之家  ›  专栏  ›  技术社区  ›  Beau Simensen

我可以在不查看每个元素的情况下将std::vector<Animal*>转换为std::vector<Dog*>吗?

  •  8
  • Beau Simensen  · 技术社区  · 15 年前

    我有一个基类,有几个类扩展了它。我有一些通用的库实用程序,它创建一个包含指向基类的指针的向量,这样任何子类都可以工作。如何将向量的所有元素强制转换为特定的子类?

    // A method is called that assumes that a vector containing
    // Dogs casted to Animal is passed.
    void myDogCallback(vector<Animal*> &animals) {
        // I want to cast all of the elements of animals to
        // be dogs.
        vector<Dog*> dogs = castAsDogs(animals);
    }
    

    // A method is called that assumes that a vector containing
    // Dogs casted to Animal is passed.
    void myDogCallback(vector<Animal*> &animals) {
        // I want to cast all of the elements of animals to
        // be dogs.
        vector<Dog*> dogs;
        vector<Animal*>::iterator iter;
        for ( iter = animals.begin(); iter != animals.end(); ++iter ) {
            dogs.push_back(dynamic_cast<Dog*>(*iter));
        }
    }
    
    7 回复  |  直到 15 年前
        1
  •  12
  •   Kirill V. Lyadvinsky    15 年前

    你可以利用 std::transform . 它仍然在使用 for()

    #include <vector>
    #include <algorithm>
    using namespace std;
    
    struct Animal { virtual ~Animal() {} };
    struct Dog : Animal { virtual ~Dog() {} };
    
    template<typename Target>
    struct Animal2Target { Target* operator ()( Animal* value ) const { return dynamic_cast<Target*>(value); } };
    
    void myDogCallback(vector<Animal*> &animals) {
    {
        vector<Dog*> dogs;
        transform( animals.begin(), animals.end(), dogs.begin(), Animal2Target<Dog>() );
    }
        2
  •  0
  •   oz10    15 年前

    当动物向量包含其他动物特化时,您编写的代码将在dogs向量中放入一堆空指针。

    vector<Dog*> dogs;
    vector<Animal*>::iterator iter;
    Dog* dog;
    
    for( iter = animals.begin(); iter != animals.end(); ++iter )
    {
      dog = dynamic_cast<Dog*>(*iter);
      if( dog )
      {
        dogs.push_back( dog );
      }
    }
    
        3
  •  0
  •   Richard Corden    15 年前

    remove_copy_if . 我无法解释他们为什么这么叫它,但是它将元素从一个容器复制到另一个容器 满足谓词。以下是基本想法(未经测试):

    struct IsDog : unary_function < Animal *, bool > {
      bool operator ()(Animal * animal) const {
        return dynamic_cast <Dog*> (animal);
      }
    };
    
    void foo (vector<Animal*> animals) {
      vector<Dog*> dogs;
      std::remove_copy_if (animals.begin ()
        , animals.end ()
        , back_inserter (dogs)
        , std::not1 ( IsDog () ) );  // not1 here negates the result of IsDog!
    
    
      // dogs now contains only animals that were dogs
    

    }

    我想从一个角度来看 删除“复制”如果 把它想象成 copy_unless .

    如果代码只基于迭代器,另一种方法是将vector<Animal*>的迭代器包装为只返回集合中的狗的迭代器。这里的关键优势是你仍然只有一个容器,但是你当然要多付一点钱,因为你的算法可以在整个动物集合中导航。

    class dog_iterator  // derive from std::iterator probably with bidirectinoal tag
    {
    private:
      vector<Animals*>::iterator getNextDogIter (vector<Animals*>::iterator iter) {
        while (iter != m_end) {
          if (0 != dynamic_cast<Dog*> (*iter)) {
            break;
          }
          ++iter;
        }
        return iter;
      }
    
    public:
      dog_iterator (vector<Animals*>::iterator iter, vector<Animals*>::iterator end)
      : m_end (end)
      , m_iter (getNextDogIter (iter))
      {
      }
    
      // ... all of the usual iterator functions
    
      dog_iterator & operator++ ()
      {
        // check if m_iter already is at end - otherwise:
        m_iter = getNextDogIter (m_iter + 1);
        return *this;
      }
      // ...
    };
    

        4
  •  0
  •   Sad Developer    15 年前

    通常情况下,使用动态浇铸不是很好。您可能应该重构代码,这样就不需要使用显式向下转换。

    CPP FAQ lite 了解更多信息。

    UPD公司 Stroustrup page (搜索“Why can't I assign a vector to a vector?”)

        5
  •  0
  •   Troubadour    15 年前

    static_cast

    void myDogCallback(vector<Animal*> &animals) {
    
        const vector<Animal*>::size_type numAnimals = animals.size();
    
        vector<Dog*> dogs;
        dogs.reserve( numAnimals );
    
        for ( vector<Animal*>::size_type i = 0; i < numAnimals; ++i ) {
            dogs.push_back(static_cast<Dog*>( animals[i] ));
        }
    }
    

    dynamic_cast 但是,事实上,如果你能保证它的类型,那么它是完全安全的,我认为这是明智之举。

    push_back . 作为一个替代循环,我使用索引只是因为我一直认为使用索引进行迭代必须比迭代器快,但这可能是胡说八道:)

        6
  •  0
  •   PaulProgrammerNoob    7 年前

    当你能保证,你的 std::vector<Animal*> Dog* 你可以用 reinterpret_cast

        7
  •  0
  •   Escualo    7 年前

    混合 std::transform static_cast (因为您确信它的安全性)可能看起来像:

    std::transform(animals.begin(), animals.end(),
                   std::back_insert_iterator<std::vector<Dog*>>(dogs),
                   [](auto ptr) { return static_cast<Dog*>(ptr); });