[英]I want a vector of derived class pointers as base class pointers
在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)
^
std::vector
有一個復制構造函數,但它要求你復制完全相同類型的向量。 幸運的是,還有另一個構造函數,它接受一對迭代器並添加范圍內的所有元素,因此您可以這樣做:
vector<Animal*> animals(dogs.begin(),dogs.end());
這通過迭代每個Dog
指針創建一個Animal
指針的新向量。 每個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()}
};
}
你可以毫無問題地做你真正想做的事! 也就是說,只需:
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,是的,但是std::vector<X>
不會繼承自std::vector<Y>
- 無論X和Y如何相關!
模板不會給矢量帶來太多的智能; 他們只是定義了一個新類。 你可以這樣想:
class vectorOfDogs {
Dog* myDogs;
//...
}
class vectorOfAnimals {
Animal* myAnimals;
//...
}
是否vectorOfDogs
從inhert vectorOfAnimals
? 顯然不是! 但是所做的就是將類的名稱從std::vector<Dog*>
更改為vectorOfDogs
。
接受的解決方案很好,但有一個很大的缺點:它保留了相關載體內容的副本。 任何一個向量更新時,我們都需要更新冗余數據以保持全局狀態一致。 不是很喜歡,決定試圖解決這個問題(不幸的是需要發現這需要相當多的工作......):
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)反向迭代器,...
你可以創建你的狗矢量:
vector<Animal*> dogs;
並在插入之前投射你的狗對象
dogs.push_back((Animal*)new Dog());
之后,在訪問時退回
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.