这段代码安全吗?将 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<std::vector<A*>
转换为std::shared_ptr<std::vector<B*>
问题是std::vector<B>
没有从std::vector<A>
继承,smart_ptr
也没有。所以我做了一个可怕的演员表:
std::shared_ptr<VectorA> vector_a = * ((std::shared_ptr<VectorA>*)&vector_b);
代码在那里编译并运行,但是安全吗? http://liveworkspace.org/code/3dQTz1$0
【问题讨论】:
简短回答:不。它基本上是对共享指针的重新解释,所以谁知道它会做什么。 至少使用reinterpret_cast
,而不是这里的C风格转换。不,这不是标准定义的(这并不意味着它不起作用,只是不能保证)。
另外,一个非智能指针向量也在等待泄漏(并且可能是)。使用vector<shared_ptr<A>>
或其他东西,因为那样你就不会泄漏(如果需要多态行为),或者如果你不使用vector<A>
。
@Kevin :是的,很抱歉。我已经简化了我的代码;我没有使用 std::vector 而是自定义模板类。
【参考方案1】:
这是一个可怕的解决方案。当您将一种类型的对象向量转换为另一种类型时,您可能会遇到各种不正确/未定义的行为。
你应该从一开始就使用std::shared_ptr<A>
的向量,并用指向B
类型对象的指针初始化它们。
如果不可能,您可以创建一个持有std::weak_ptr<A>
的新向量,以避免两次管理这些对象。
【讨论】:
【参考方案2】:不要那样做。
考虑(指向)B
的向量不是(指向)A
的向量,或者更准确地说,不是(指向)@987654323 的向量的普遍有效替代@;因此,您不能执行这样的转换是有充分理由的。
虽然你在B
的向量中拥有的确实是一组对象,它们也是(A
的实例,请考虑以下算法:
void f(vector<A>& v)
A a;
v.push_back(a);
现在假设您使用B
的向量调用f()
。这将尝试将不是B
的类的实例添加到应该只包含B
类型的元素的集合中。
这里的解决方案是使只接受vector<A>
的代码足够灵活,也可以在vector<B>
上工作。换句话说,您需要将其设为模板。例如,您的模板只能接受从A
派生的类型的向量作为参数。这很容易通过一些 SFINAE 技术和类型特征(例如 std::is_base_of
)来强制执行。
【讨论】:
【参考方案3】:严格来说,取消引用操作应该会成功。两个向量都是指针容器,因此将一个向量转换为另一个,虽然对于生产代码是不可接受的,但仍将使用相同的尺寸和对齐方式。
C++ 提供了丰富的抽象来避免这些恶作剧,但最好将派生对象的向量填充为指向基类的指针。
【讨论】:
【参考方案4】:你仍然可以投射元素:
A* a = vector_b->at (42);
给你你想要的。
现在,如果您编写了以shared_ptr<vector<A*>>
作为参数的函数,那么您有多种解决方案可以帮助您摆脱这种情况:
shared_ptr<A*[]>
而不是shared_ptr<vector<A*>>
(它可以随心所欲)
将A**
作为参数(并使用vector_b->data ()
):它不会将您与共享指针联系起来。
更好:将取消引用的迭代器作为指向A
的[智能] 指针(回想一下operator->
链)
使用std::vector<std::shared_ptr<A>>
(浪费资源)
【讨论】:
以上是关于这段代码安全吗?将 std::vector<Derived*> 强制转换为 std::vector<Base*>的主要内容,如果未能解决你的问题,请参考以下文章
为什么c ++用零来初始化std :: vector,而不是std :: array?
将两个 std::vector<cv::Point> 向量和安全公共点与第三个 std::vector<cv::Point> 进行比较