编译器如何复制作为数组的成员数据?

How does the compiler copy member data that are arrays?

提问人:Itachi Uchiwa 提问时间:8/15/2021 更新时间:8/16/2021 访问量:344

问:

AFAIK,无法复制或分配数组,因此:

int a[5] = {1, 2};// 1 2 0 0 0
int b = a;// error
b = a; // error

但是编译器如何通过简单的 copy-ctor 和 copy-assignment 运算符复制作为类/结构类型成员数据的数组呢?

// trivial struct
struc Arr{
    int arr_[7];
};

Arr a = {5, 7, 2, 10, 9, 1, 2};
Arr b = a;// trivial cpy-ctor
a = b; // trivial cpy-assignment operator
  • 那么编译器是否执行类似以下操作: ?或者它遍历 的所有元素并将它们 cpy-assign 给它们在 中的对应元素?this->arr_ = rhs.arr_rhs.arr_rhs
C++ 数组类 构造函数 复制 赋值

评论

0赞 Jonathon Reinhart 8/15/2021
无论发生什么,都是在发出的汇编代码中完成的,而不是在 C 语言级别完成的。
1赞 Eljay 8/15/2021
在这种情况下(其中类 和 ),编译器将执行 .is_standard_layoutis_trivialmemcpy
0赞 tueda 8/15/2021
请参阅默认的复制构造函数规范 11.4.4.2 [class.copy.ctor]:timsong-cpp.github.io/cppwp/n4861/class.copy.ctor#14,尤其是 (14.1)。复制赋值运算符 11.4.5 [class.copy.assign]:timsong-cpp.github.io/cppwp/n4861/class.copy.assign#12
0赞 cup 8/15/2021
当你做 a=b 或 b=a 时,你不会像 for like 那样复制,b 是一个整数,a 是一个数组。不能将整数复制到数组中,也不能将数组复制到整数中。
1赞 HolyBlackCat 8/16/2021
“琐碎的复制”是指“形成按位复制的复制构造函数”,对吧?不是“编译器生成的复制构造函数”?

答:

2赞 Sam Varshavchik 8/15/2021 #1

所有数组和所有对象,都归结为内存中的位和字节。唯一的区别是哪些特定的位和字节,以及它们的解释和使用方式

所有位和字节都完全相同。没有特殊的人,也没有弱势的人。

复制这些位和字节是计算机所做的。编译器只需发出适当的指令,将适当的位和字节从连续内存的一部分复制到连续内存的另一部分。与等效循环非常相似,在数组中的每个值上,都可以复制它。C++ 标准不要求编译器以任何特定方式实现它的任何部分。C++ 标准定义了语言每个部分的效果结果(以及所需的条件)。编译器如何做到这一点,完全由编译器决定。你不在乎它。这是编译器的责任。

C++ 编程语言的规则禁止以这种方式复制单个数组。为什么会这样,那将是一个不同的问题,但就这个问题而言,它只是禁止这样做的规则。

但它们确实允许默认构造函数和赋值运算符影响数组成员的复制/移动。

稍后,您的 C++ 教科书将向您介绍模板。您将学习如何像使用数组一样使用它。要分辨出其中的区别是非常非常困难的。它就像一个数组,正确编写的代码将像数组一样使用它。但它不是一个数组。因此,可以像复制和移动等效数组一样复制/移动它。std::array

评论

0赞 Itachi Uchiwa 8/16/2021
是的,当然,我曾经像任何其他 STL 容器一样使用它,它真的非常强大,并且克服了内置数组的限制,例如复制/移动、分配和传递它/将其传入和返回函数。std::array
2赞 HolyBlackCat 8/15/2021 #2

那么编译器是否执行类似以下操作: ?this->arr_ = rhs.arr_

不。正如你所说,这不会编译。

或者它遍历 的所有元素并将它们复制分配给它们在 ?rhs.arr_lhs

对于复制分配运算符 - 是。

对于复制构造函数 - 否。在那里,每个元素都是从另一个数组的相应元素复制构造的(而不是赋值的)。喜欢这个:

Arr(const Arr &other) : arr_{other.arr_[0], other.arr_[1], ..., other.arr_[6]} {}

手动编写的循环是不可能做到的。如果要手动执行,则需要手动拼写每个元素。

但是,当然,如果复制构造函数和复制赋值都是微不足道的(这意味着它们有效地逐字节复制结构,而不做任何其他事情),那么复制构造函数是复制构造每个元素还是复制赋值它并不重要,因为两者都做同样的事情。

Note that being trivial is unrelated to being compiler-generated. The question gave me a feel that there might some confusion here.

评论

0赞 Itachi Uchiwa 8/16/2021
Thank you for such good explanation. One last thing: The way you showed on copy-ctor to copy an array is interesting: . So If I have an array of a big size let's say 1 million elements then should I hard-code the initializers in the copy-ctor? It is really insane and whether there's a workaround? Thank you!arr_{other.arr_[0], other.arr_[1], ..., other.arr_[6]}
1赞 HolyBlackCat 8/16/2021
@ItachiUchiwa The question is why are you writing a copy constructor in the first place? The rule of zero says to not do it unless absolutely necessary. You can also try using in place of a plain array, it can be copy-constructed using just . You could also just use a loop in the copy constructor (there will be overhead with some types, but not with trivial ones like ).std::array: arr_(other.arr_)int
0赞 Itachi Uchiwa 8/16/2021
For example if my class needs a copy-ctor because some member data needs deep copy.
0赞 HolyBlackCat 8/16/2021
@ItachiUchiwa Consider wrapping that single data member in a separate class with appropriate copy constructor and assignment, then you don't need them in the class with the array.