28

考虑这些类。

class Base
{
   ...
};

class Derived : public Base
{
   ...
};

这个函数

void BaseFoo( std::vector<Base*>vec )
{
    ...
}

最后是我的矢量图

std::vector<Derived*>derived;

我想传递derived给 function BaseFoo,但编译器不让我。我如何解决这个问题,而不将整个向量复制到 a std::vector<Base*>

4

9 回答 9

44

vector<Base*>并且vector<Derived*>是不相关的类型,所以你不能这样做。这在此处的 C++ 常见问题解答中进行了解释。

您需要将变量从 a 更改vector<Derived*>为 avector<Base*>并将Derived对象插入其中。

此外,为避免vector不必要地复制,您应该通过 const-reference 传递它,而不是按值传递:

void BaseFoo( const std::vector<Base*>& vec )
{
    ...
}

最后,为了避免内存泄漏,并使您的代码异常安全,请考虑使用旨在处理堆分配对象的容器,例如:

#include <boost/ptr_container/ptr_vector.hpp>
boost::ptr_vector<Base> vec;

或者,将向量更改为保存智能指针而不是使用原始指针:

#include <memory>
std::vector< std::shared_ptr<Base*> > vec;

或者

#include <boost/shared_ptr.hpp>
std::vector< boost::shared_ptr<Base*> > vec;

在每种情况下,您都需要BaseFoo相应地修改您的功能。

于 2008-09-22T13:31:38.607 回答
27

与其传递容器对象 ( vector<>),不如传入beginend迭代器,就像其他 STL 算法一样。接收它们的函数将被模板化,并且传入 Derived* 或 Base* 都没有关系。

于 2008-09-22T14:10:54.767 回答
14

此问题发生在具有可变容器的编程语言中。你不能把一袋可变的苹果当作一袋水果来传递,因为你不能确定其他人没有将柠檬放入那袋水果中,之后它就不再符合一袋苹果的条件。如果这袋苹果不是可变的,那么将它作为一袋水果传递就可以了。搜索协方差/逆变。

于 2008-09-22T17:06:28.793 回答
8

一种选择是使用模板

template<typename T>
void BaseFoo( const std::vector<T*>& vec)
{
 ...
}

缺点是实现必须在头文件中,你会得到一点代码膨胀。您最终会为每种类型实例化不同的函数,但代码保持不变。根据用例,这是一个快速而肮脏的解决方案。

编辑,我应该注意我们在这里需要一个模板的原因是因为我们正试图为不相关的类型编写与其他几位海报所指出的相同的代码。模板允许您解决这些确切的问题。我还更新了它以使用 const 引用。当您不需要副本时,您还应该通过 const 引用传递“重”对象,例如向量,这基本上总是如此。

于 2008-09-22T13:23:07.923 回答
2

通常你会从一个基指针容器开始,而不是相反。

于 2008-09-22T13:27:17.430 回答
2

如果您与第三方库打交道,这是您唯一的希望,那么您可以这样做:

BaseFoo (*reinterpret_cast<std::vector<Base *> *>(&derived));

否则,请使用其他建议之一修复您的代码。

于 2008-09-22T13:39:43.813 回答
2

从上面获取Matt Price 的答案,假设您事先知道要在函数中使用哪些类型,您可以在头文件中声明函数模板,然后为这些类型添加显式实例化:

// BaseFoo.h
template<typename T>
void BaseFoo( const std::vector<T*>& vec);

// BaseFoo.cpp
template<typename T>
void BaseFoo( const std::vector<T*>& vec);
{
 ...
}

// Explicit instantiation means no need for definition in the header file.
template void BaseFoo<Base> ( const std::vector<Base*>& vec );
template void BaseFoo<Derived> ( const std::vector<Derived*>& vec );
于 2008-09-22T15:06:48.283 回答
1

如果std::vector支持您的要求,则可以在不使用任何强制转换的情况下击败 C++ 类型系统(编辑:ChrisN 到 C++ FAQ Lite 的链接讨论了相同的问题):

class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};

void pushStuff(std::vector<Base*>& vec) {
    vec.push_back(new Derived2);
    vec.push_back(new Base);
}

...
std::vector<Derived1*> vec;
pushStuff(vec); // Not legal
// Now vec contains a Derived2 and a Base!

由于您的BaseFoo()函数按值获取向量,因此它无法修改您传入的原始向量,所以我写的内容是不可能的。但是,如果它需要一个非常量引用并且您使用它reinterpret_cast<std::vector<Base*>&>()来传递您的std::vector<Derived*>,您可能无法获得您想要的结果,并且您的程序可能会崩溃。

Java 数组支持协变子类型,这需要 Java每次在数组中存储值时都进行运行时类型检查。这也是不可取的。

于 2008-09-22T15:00:02.810 回答
0

它们是不相关的类型——你不能。

于 2008-09-22T13:23:09.780 回答