我可以将std :: vector 转换为std :: vector 而不必查看每个元素?
我有一个基类,有几个类扩展它。我有一些通用库实用程序创建一个包含指向基类的指针的向量,以便任何子类都可以工作。我如何将矢量的所有元素转换为特定的子类?我可以将std :: vector <Animal*>转换为std :: vector <Dog*>而不必查看每个元素?
// 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));
}
}
您的代码。
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);
}
}
通常使用dynamic_cast进行向下转换不是很好。你应该重构你的代码,这样你就不需要使用明确的向下转换。
有关更多信息,请参见CPP FAQ lite。
UPD此外,请参阅Stroustrup page(搜索 “为什么我不能指定一个矢量到矢量?”)
你可以使用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>());
}
有两个选项。最简单的方法是使用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
}
我想一个方式来看待remove_copy_if
是把它作为copy_unless
。
另一种方法是,如果您仅将代码基于迭代器,则将包含矢量< 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;
}
// ...
};
这很粗糙,但我希望它能向你展示基本原理。
如果你说你能保证每一个元素是一个真正的狗那么就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]));
}
}
我通常总是从人们下意识的反应,这是不好的,你应该总是使用但实际上,如果你可以对这种类型做出保证,那么它是完全安全的,而且IMO是一个明智的做法。
此外,您的保证意味着新的矢量具有相同的大小,因此保留相同的空间以避免在每个push_back
中进行任何分配。作为一种替代回路我使用的索引仅仅是因为我一直认为使用索引必须大于一个迭代更快的迭代,但是这可能是废话:)
当你能保证,你std::vector<Animal*>
只包含Dog*
可以使用reinterpret_cast
。
混合的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); });
重复:http://stackoverflow.com/questions/902667/stl-container-assignment-和 - 常量指针 – GManNickG 2009-07-24 03:39:32
这是不是很愚蠢 - 请注意,他不是从`矢量`复制到`矢量 `,但反过来! –
2009-07-24 03:45:21