C++ 这是未定义的行为吗?

C++ 这是未定义的行为吗?,c++,undefined-behavior,C++,Undefined Behavior,我在我们的代码库中找到了以下代码。我的同事认为这没关系,但看起来很可疑。是不是乌布 class A { //some stuff }; class B : public A { int a; int b; int c; } void foo( std::vector<A>& a ) { std::vector<B> b; for(size_t i = 0 ; i < a.size(); ++i ){ b.

我在我们的代码库中找到了以下代码。我的同事认为这没关系,但看起来很可疑。是不是乌布

class A {
   //some stuff
};

class B : public A {
   int a;
   int b;
   int c;
}

void foo( std::vector<A>& a ) {

   std::vector<B> b;
   for(size_t i = 0 ; i < a.size(); ++i ){
      b.push_back( *(B*)(&a[i]) );
   }

   //remove some elements from b

   for(size_t i = 0 ; i < b.size(); ++i ){
      a.push_back( *(A*)(&b[i]) );
   }

}
A类{
//一些东西
};
B类:公共A{
INTA;
int b;
INTC;
}
void foo(标准::向量&a){
std::载体b;
对于(大小i=0;i
这是未定义的行为。原始向量中的真实对象是
A
,而不是
B
,因此强制转换不正确,您将获得未定义的行为


该代码最常见的结果可能是数据不正确(从向量中的下一个对象(如果存在)或从以下内存位置读取
B
中不存在的
A
的成员)或崩溃(如果它恰好是最后一个元素,则原始向量中没有保留额外的空间,读取恰好扩展到受保护的内存页。

这是未定义的行为。原始向量中的真实对象是
a
,而不是
B
,因此转换不正确,您将获得未定义的行为


该代码最常见的结果可能是数据不正确(从向量中的下一个对象(如果存在)或从以下内存位置读取
B
中不存在的
A
的成员)或崩溃(如果它恰好是最后一个元素,则原始向量中没有保留额外的空间,并且读取恰好扩展到受保护的内存页。

简短回答-是,这是未定义的行为

删除向量可以使这一点更清楚:

void foo( A a ) {
  B b;
  b = *(B*)(&a);
  a = *(A*)(&b);
}
就涉及的内存问题而言,上述版本与您的版本相当。最后一个语句(分配给
a
)实际上很好。这是一个多态性的向上转换,实际上您甚至不需要所有额外的转换。这很好:

a = *&b;
第一个赋值-to
b
-未定义。您正在尝试非法的多态向下转换。这可能是无害的,所有额外的转换都将迫使编译器接受它。但这肯定是未定义的行为

一般来说,如果你使用C++类的C++类,你就有麻烦了。尝试这种类型的下流是用动态的:
b = *(dynamic_cast<B*>(&a));

简短回答-是的,这是未定义的行为

删除向量可以使这一点更清楚:

void foo( A a ) {
  B b;
  b = *(B*)(&a);
  a = *(A*)(&b);
}
就涉及的内存问题而言,上述版本与您的版本相当。最后一个语句(分配给
a
)实际上很好。这是一个多态性的向上转换,实际上您甚至不需要所有额外的转换。这很好:

a = *&b;
第一个赋值-to
b
-未定义。您正在尝试非法的多态向下转换。这可能是无害的,所有额外的转换都将迫使编译器接受它。但这肯定是未定义的行为

一般来说,如果你使用C++类的C++类,你就有麻烦了。尝试这种类型的下流是用动态的:
b = *(dynamic_cast<B*>(&a));


看起来是这样,
*(B*)(&a[i])
读取的数据超出了
a[i]
的内存。对于最后一个元素,您读取的数据超出了自己的内存。
push_back
将使用
B
的复制语义复制
a
,这(除非您没有复制a、B或c)肯定会读取未初始化的内存。@LuchianGrigore不是已经解除防护的UB吗?@bennofs是的,第一个
*
…这不能在生产代码中。这是一个疯狂的发现。看起来,
*(B*)(&a[i])
读取
a[i]之外的数据
的内存。对于最后一个元素,您将读取超出自己内存的数据。
向后推
将使用
B
的复制语义复制
A
,这(除非您没有复制A、B或c)肯定会读取未初始化的内存。@LuchianGrigore不是已经解除保护的UB吗?@bennofs是的,第一个
*
…这不能在生产代码中。这是疯狂的发现。如果B的复制构造函数不复制a、B或c,这一定是真的吗?那么我就看不到在分配的内存之外读取。(不,我绝对不是在提倡这个代码,只是想知道技术细节)@JoachimIsaksson:在上面的代码中,复制构造函数是隐式定义的,它将尝试复制。在另一种情况下,您可能会实现一个不涉及这些成员的复制构造函数,但这至少会很尴尬……为什么这些成员不重要?如果您希望支持从
对象复制,请u可以只实现一个构造函数,使用一个<代码> < /code >。对于默认复制构造函数,确实没有考虑。只是要求在 ub和<代码> HHC (恐怖恐怖代码)之间清楚地画出一行。@JoachimIsaksson:好吧,手头上的标准是UB。如果指针没有引用派生对象的基子对象,则从指向基的指针到指向派生对象的指针的
静态转换是未定义的。5.2.9p11[…]如果类型为“指向cv1 B的指针”的PR值指向实际上是D类型对象的子对象的B,结果指针指向D类型的封闭对象。否则,强制转换的结果是未定义的。@lezebulon和支持内存保护或虚拟内存的硬件,最肯定的是,是的。如果B的复制构造函数不复制a、B或c,这是否一定是真的?那么我就看不到分配内存之外的读取。(不,我绝对不提倡这种代码,只是想知道技术细节)@JoachimIsaksson:在上面的代码中,复制构造函数是隐式定义的,它将尝试复制。在另一种情况下,您可能会实现一个不涉及这些成员的复制构造函数,但这至少会让人尴尬……为什么这些成员