9

例如,它让我感到困惑:

struct A {
//  some fileds...
    char buf[SIZE];
};

A a;
a = a;

通过 A 的 field 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++ 对象中默认的赋值操作行为是什么?

4

4 回答 4

13

赋值运算符不是根据memcpy(§12.8/28) 定义的。

非联合类 X 的隐式定义的复制/移动赋值运算符执行其子对象的成员复制/移动赋值。首先分配 X 的直接基类,按照它们在基说明符列表中的声明顺序,然后按照它们在类定义中声明的顺序分配 X 的直接非静态数据成员. 令 x 为函数的参数,或者对于移动运算符,为引用参数的 xvalue。每个子对象都以适合其类型的方式分配:

[...]

— 如果子对象是一个数组,则以适合元素类型的方式分配每个元素;

[...]

如您所见,每个char元素都将被单独分配。那总是安全的。

但是,在 as-if 规则下,编译器可以用 a 替换它,memmove因为它对char数组具有相同的行为。memcpy如果它可以保证会导致相同的行为,它也可以用 a 替换它memcpy,即使理论上这样的事情是未定义的。编译器可以依赖理论上未定义的行为;存在未定义行为的原因之一是编译器可以将其定义为更适合其操作的​​任何内容。

实际上,在这种情况下,编译器可以进一步采用 as-if 规则,并且根本不对数组做任何事情,因为这也会导致相同的行为。

于 2013-08-06T12:22:27.547 回答
3

默认分配(和复制)行为不会 memcpy 整个类,这会破坏事情。每个成员都使用其复制构造函数或赋值运算符(取决于操作)进行复制。这以递归方式应用于成员及其成员。当达到基本数据类型时,它只是执行数据的直接复制,类似于 memcpy。因此,可以像 memcpy 一样复制基本数据类型的数组,但整个类不是。如果你将 std::string 添加到你的类中,它的 = 运算符将被调用,以及数组的副本。如果您使用 std::string 数组,则数组中的每个字符串都会调用它们的运算符。他们不会记忆。

于 2013-08-06T12:14:45.737 回答
0

一些有限的实验告诉我,g++ 完全消除了任何复制尝试a = a;[假设它很明显——我确信指针已经足够混乱,最终将有可能在自身上复制相同的对象,并获得未定义的行为]。

于 2013-08-06T12:22:58.050 回答
-1

如果使用 memcpy,可能会出现一些未定义的行为。

这是如何复制给定类的实现细节。memcpy() 函数和复制构造函数都会被转换成一些机器码。但是,您在内存中的对象不应重叠,因为默认分配并不能保证在它们重叠的情况下您将获得正确的结果。

那么,C++ 对象中默认的赋值操作行为是什么?

与其他响应一样,这种行为会递归地调用所有类/结构成员的赋值。但是从技术上讲,就像您的情况一样,它可能只是复制整个内存块,特别是如果您的结构是 POD(普通旧数据)。

于 2013-08-06T12:28:04.313 回答