这段代码安全吗?将 std::vector<Derived*> 强制转换为 std::vector<Base*>

Posted

技术标签:

【中文标题】这段代码安全吗?将 std::vector<Derived*> 强制转换为 std::vector<Base*>【英文标题】:is this code safe? cast a std::vector<Derived*> to std::vector<Base*> 【发布时间】:2013-02-05 21:45:09 【问题描述】:

基本上我有一个class A 和一个class B : public A

我想将std::shared_ptr&lt;std::vector&lt;A*&gt; 转换为std::shared_ptr&lt;std::vector&lt;B*&gt;

问题是std::vector&lt;B&gt; 没有从std::vector&lt;A&gt; 继承,smart_ptr 也没有。所以我做了一个可怕的演员表

  std::shared_ptr<VectorA> vector_a = * ((std::shared_ptr<VectorA>*)&vector_b);

代码在那里编译并运行,但是安全吗? http://liveworkspace.org/code/3dQTz1$0

【问题讨论】:

简短回答:不。它基本上是对共享指针的重新解释,所以谁知道它会做什么。 至少使用reinterpret_cast,而不是这里的C风格转换。不,这不是标准定义的(这并不意味着它不起作用,只是不能保证)。 另外,一个非智能指针向量也在等待泄漏(并且可能是)。使用vector&lt;shared_ptr&lt;A&gt;&gt; 或其他东西,因为那样你就不会泄漏(如果需要多态行为),或者如果你不使用vector&lt;A&gt; @Kevin :是的,很抱歉。我已经简化了我的代码;我没有使用 std::vector 而是自定义模板类。 【参考方案1】:

这是一个可怕的解决方案。当您将一种类型的对象向量转换为另一种类型时,您可能会遇到各种不正确/未定义的行为。

你应该从一开始就使用std::shared_ptr&lt;A&gt;的向量,并用指向B类型对象的指针初始化它们。

如果不可能,您可以创建一个持有std::weak_ptr&lt;A&gt; 的新向量,以避免两次管理这些对象。

【讨论】:

【参考方案2】:

不要那样做。

考虑(指向)B 的向量不是(指向)A 的向量,或者更准确地说,不是(指向)@987654323 的向量的普遍有效替代@;因此,您不能执行这样的转换是有充分理由的。

虽然你在B 的向量中拥有的确实是一组对象,它们也是(A 的实例,请考虑以下算法:

void f(vector<A>& v)

    A a;
    v.push_back(a);

现在假设您使用B 的向量调用f()。这将尝试将不是B 的类的实例添加到应该只包含B 类型的元素的集合中。

这里的解决方案是使只接受vector&lt;A&gt; 的代码足够灵活,也可以在vector&lt;B&gt; 上工作。换句话说,您需要将其设为模板。例如,您的模板只能接受从A 派生的类型的向量作为参数。这很容易通过一些 SFINAE 技术和类型特征(例如 std::is_base_of)来强制执行。

【讨论】:

【参考方案3】:

严格来说,取消引用操作应该会成功。两个向量都是指针容器,因此将一个向量转换为另一个,虽然对于生产代码是不可接受的,但仍将使用相同的尺寸和对齐方式。

C++ 提供了丰富的抽象来避免这些恶作剧,但最好将派生对象的向量填充为指向基类的指针。

【讨论】:

【参考方案4】:

你仍然可以投射元素:

A* a = vector_b->at (42);

给你你想要的。

现在,如果您编写了以shared_ptr&lt;vector&lt;A*&gt;&gt; 作为参数的函数,那么您有多种解决方案可以帮助您摆脱这种情况:

使用shared_ptr&lt;A*[]&gt; 而不是shared_ptr&lt;vector&lt;A*&gt;&gt;(它可以随心所欲) 将A** 作为参数(并使用vector_b-&gt;data ()):它不会将您与共享指针联系起来。 更好:将取消引用的迭代器作为指向A 的[智能] 指针(回想一下operator-&gt; 链) 使用std::vector&lt;std::shared_ptr&lt;A&gt;&gt;(浪费资源)

【讨论】:

以上是关于这段代码安全吗?将 std::vector<Derived*> 强制转换为 std::vector<Base*>的主要内容,如果未能解决你的问题,请参考以下文章

C++ 的 std::vector 和线程安全

C ++ - std :: vector安全多线程

为什么c ++用零来初始化std :: vector,而不是std :: array?

将两个 std::vector<cv::Point> 向量和安全公共点与第三个 std::vector<cv::Point> 进行比较

对 std::vector 元素的赋值是线程安全的吗?

在 std::vector 上的 Openmp 和缩减?