例如,它让我困惑:
struct A {
// some fileds...
char buf[SIZE];
};
A a;
a = a;
通过A的字段buf
,看起来默认的赋值操作可能会调用类似memcpy
的东西来将对象X分配给Y,那么如果将对象分配给自身并且没有定义了显式赋值操作,如上面的a = a;
。
memcpy手册页:
DESCRIPTION
The memcpy() function copies n bytes from memory area src to memory area dest. The memory areas must not overlap. Use memmove(3) if the memory areas do overlap.
如果使用memcpy
,可能会出现一些未定义的行为。
那么,C ++对象中的默认赋值操作行为是什么?
答案 0 :(得分:12)
赋值运算符未按memcpy
(§12.8/ 28)定义。
非联合的隐式定义的复制/移动赋值运算符 class X执行其子对象的成员复制/移动分配。 首先按照它们的顺序分配X的直接基类 base-specifier-list中的声明,然后是immediate X的非静态数据成员按它们的顺序分配 在类定义中声明。设x是参数 函数或者,对于移动运算符,xvalue指的是 参数。每个子对象都以适合其的方式分配 类型:
[...]
- 如果子对象是一个数组,则在每个元素中分配 适合元素类型的方式;
[...]
如您所见,每个char
元素都将单独分配。这总是安全的。
但是,在as-if规则下,编译器可能会将其替换为memmove
,因为它对char
数组具有相同的行为。如果它可以保证memcpy
将导致相同的行为,即使理论上这样的事情未定义,它也可以用memcpy
替换它。编译器可以依赖于理论上未定义的行为;存在未定义行为的原因之一是编译器可以将其定义为更适合其操作的任何内容。
实际上,在这种情况下,编译器可以进一步采用as-if规则,根本不对数组做任何事情,因为这也会导致相同的行为。
答案 1 :(得分:2)
默认的赋值(和复制)行为不会记忆整个类,这会破坏事物。使用其复制构造函数或赋值运算符(取决于操作)复制每个成员。这是对成员及其成员递归应用的。当达到基本数据类型时,它只是执行数据的直接副本,类似于memcpy。因此可以复制类似于memcpy的基本数据类型数组,但整个类不是。如果将std :: string添加到类中,则会调用其=运算符以及数组副本。如果使用了std :: string数组,则数组中的每个字符串都将调用其运算符。他们不会记忆。
答案 2 :(得分:0)
一些有限的实验告诉我,g ++完全删除了任何复制a = a;
的尝试[假设很明显 - 我确信对指针有足够的麻烦,最终可能会将相同的对象复制到自身上,并获得未定义的行为]。
答案 3 :(得分:-1)
如果使用memcpy,可能会出现一些未定义的行为。
这是一个实现细节,如何复制给定的类。 memcpy()函数和复制构造函数都将转换为一些机器代码。但是,内存中的对象不应重叠,因为默认分配并不能保证在重叠时它们会得到正确的结果。
那么,C ++对象中的默认赋值操作行为是什么?
与其他响应一样,行为是这样的,它将以递归方式调用所有类/结构成员的赋值。但从技术上讲,就像你的情况一样,它可能只是复制整个内存块,特别是如果你的结构是POD(普通旧数据)。