23

在 C++ 中,vector 类存储一个对象数组。在这种情况下,我存储指向派生类对象(Dogs)的指针。在某些时候,我想将此向量视为指向基类(动物)对象的指针。这是“正确”/无争议的方式,对吗?为什么我不能这样做?

#include <vector>
using namespace std;

class Animal { }; 
class Dog : public Animal { };

int main(int argc, char *argv[]) {
    vector<Dog*> dogs;
    dogs.push_back(new Dog());
    dogs.push_back(new Dog());
    vector<Animal*> animals = dogs; // This doesn't seem to work.

    // This is really what I want to do...
    vector<Animal*> all_animals[] = {dogs, cats, birds};
}

错误:

Untitled.cpp:11:18: error: no viable conversion from 'vector<class Dog *>' to 'vector<class Animal *>'
    vector<Animal*> animals = dogs;
                    ^         ~~~~
/usr/include/c++/4.2.1/bits/stl_vector.h:231:7: note: candidate constructor not viable: no known conversion from 'vector<Dog *>' to 'const std::vector<Animal *, std::allocator<Animal *> > &' for 1st argument
  vector(const vector& __x)
  ^
4

4 回答 4

21

a 有一个复制构造函数,std::vector但它要求您复制完全相同类型的向量。幸运的是,还有另一个构造函数,它接受一对迭代器并添加范围内的所有元素,所以你可以这样做:

vector<Animal*> animals(dogs.begin(),dogs.end());

Animal这通过迭代每个指针来创建一个新的指针向量Dog。每个Dog指针都会在执行过程中转换为Animal指针。

这是一个更完整的示例(使用 C++11):

#include <vector>

struct Animal { };

struct Dog : Animal { };

struct Cat : Animal { };

struct Bird : Animal { };

int main(int,char**)
{
  Dog dog1, dog2;
  Cat cat1, cat2;
  Bird bird1, bird2;
  std::vector<Dog *> dogs = {&dog1,&dog2};
  std::vector<Cat *> cats = {&cat1,&cat2};
  std::vector<Bird *> birds = {&bird1,&bird2};
  std::vector<std::vector<Animal *>> all_animals = {
    {dogs.begin(),dogs.end()},
    {cats.begin(),cats.end()},
    {birds.begin(),birds.end()}
  };
}
于 2013-08-14T04:18:04.353 回答
3

你可以毫无问题地做你真正想做的事!也就是说,只需执行以下操作:

class Animal {
   public:
      std::string GetNoise() const = 0;
};
class Dog : public Animal {
   public:
      std::string GetNoise() const { return "Bark!"; }
};
class Cat : public Animal {
   public:
      std::string GetNoise() const { return "Meow"; }
      bool        LikesSleeping() const { return true; }
};

Dog* d = new Dog;
Cat* c = new Cat;
vector<Animal*> all_animals;
all_animals.push_back(d, c);

// then, later...

// this will print "Bark!"
std::cout << all_animals[0]->GetNoise() std::endl;

// if you know the type somehow
Cat* x = dynamic_cast<Cat*>(all_animals[1]);
const bool y = x->LikesSleeping();

您的代码没有按您期望的方式工作的原因std::vector<Dog*>是:.std::vector<Animal*>

换句话说,Dog 继承自 Animal,是的,但是 astd::vector<X>不继承自std::vector<Y>——不管 X 和 Y 是如何相关的!

模板并没有给向量带来太多智能;他们只是定义了一个新类。你可以这样想:

class vectorOfDogs {
    Dog* myDogs;
    //...
}

class vectorOfAnimals {
    Animal* myAnimals;
    //...
}

继承vectorOfDogs自? vectorOfAnimals显然不是!但是所做的只是将类的名称从 更改std::vector<Dog*>vectorOfDogs

于 2013-08-14T04:31:05.550 回答
1

公认的解决方案很好,但有一个很大的缺点:它保留了相关向量内容的副本。任何时候更新向量之一,我们都需要更新冗余数据以保持全局状态一致。不是很喜欢,决定尝试解决这个问题(不幸的是需要发现这需要相当多的工作......):

class AllAnimals
{
    struct Wrapper
    {
        virtual ~Wrapper() { }
        virtual Animal* begin() = 0;
        virtual Animal* end() = 0;
    };

    template <typename T>
    struct SpecificWrapper : Wrapper
    {
        T& animals;
        SpecificWrapper(T& animals)
                : animals(animals)
        { }
        Animal* begin() override
        {
            return *animals.begin();
        }
        Animal* end() override
        {
            return *animals.end();
        }
    };

    std::vector<std::unique_ptr<Wrapper>> wrappers;

public:
    class iterator : public std::iterator<std::forward_iterator_tag, Animal*>
    {
        friend class AllAnimals;
        decltype(wrappers)::iterator current, end;
        Animal* animal;
        iterator(decltype(current) begin, decltype(end) end)
                : current(begin), end(end)//, animal(nullptr)
        {
            while(current != end && (*current)->begin() == (*current)->end())
            {
                ++current;
            }
            animal = current == end ? nullptr : (*current)->begin();
        }
    public:
        bool operator==(iterator const& other)
        {
            return current == other.current && animal == other.animal;
        }
        bool operator!=(iterator const& other)
        {
            return !(*this == other);
        }
        iterator& operator++()
        {
            if(++animal == (*current)->end())
            {
                ++current;
                animal = current == end ? nullptr : (*current)->begin();
            }
            return *this;
        }
        iterator operator++(int)
        {
            iterator i(*this);
            ++*this;
            return i;
        }
        Animal* operator*()
        {
            return animal;
        }
        Animal* operator->()
        {
            return animal;
        }
    };

    iterator begin()
    {
        return iterator(wrappers.begin(), wrappers.end());
    }
    iterator end()
    {
        return iterator(wrappers.end(), wrappers.end());
    }

    template <typename T>
    void push_back(std::vector<T*>& v)
    {
        wrappers.emplace_back(new SpecificWrapper<decltype(v)>(v));
    }
};

到目前为止,我只实现了一个前向迭代器,可以提供更多的操作符来进行双向甚至随机访问。此外,我们可能会添加 const 迭代器,(const) 反向迭代器,...

于 2018-12-14T11:10:40.333 回答
0

您可以将您的 Dog Vector 创建为:

vector<Animal*> dogs;

并在插入狗之前投射它们

dogs.push_back((Animal*)new Dog());

稍后,在访问时回退

于 2013-08-14T04:24:35.613 回答