在另一个question中,我错误地使用术语POD来引用实际上不是POD类型的数据类型(由于具有构造函数)。现在,我已经查看了标准,找不到我想要的正确名称。我实际上也无法确保实际允许复制。
我所说的数据类型是一个POD,但可能包含函数,包括构造函数,但与等效的POD类型相比,不应改变其对齐或大小特征。
在标准的第3.9节中,它指出POD数据可以用memcpy复制到另一个对象,或者复制到字符数据和后面。没有这种非POD数据的保证。
但是,对象的对象表示在同一节中定义。它被定义为可以相信任何两个相同类型的对象都可以通过memcpy安全地复制。
所以我的问题是:
我的意思是对象类型的简单示例:
struct ex_struct
{
int a,b,c,d;
ex_struct() : a(123) { }
}
阅读C ++ 0x草案,我的结构似乎是一个平易可复制的类(9.1)。我认为这意味着memcpy是安全的。
答案 0 :(得分:7)
在C ++ 0x中,PODness的概念被分解为几个有用的类别:
平易可复制的类是一个类(草案3242,第
[class]
部分):
- 没有非平凡的副本构造函数(12.8),
- 没有非平凡的移动构造函数(12.8),
- 没有非平凡的复制赋值运算符(13.5.3,12.8),
- 没有非平凡的移动赋值运算符(13.5.3,12.8)和
- 有一个简单的析构函数(12.4)。
普通类是一个具有普通默认构造函数(12.1)并且可以轻易复制的类。
[注意:特别是,一个简单的可复制或普通的类没有虚函数或虚拟基 类。 - 结束记录]
标准布局类是一个类:
- 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
- 没有虚函数(10.3),没有虚基类(10.1),
- 对所有非静态数据成员具有相同的访问控制(第11条),
- 没有非标准布局基类
- 要么在最派生类中没有非静态数据成员,要么最多只有一个基类 非静态数据成员,或者没有包含非静态数据成员的基类,
- 没有与第一个非静态数据成员相同类型的基类。
琐碎的构造函数,赋值运算符和析构函数的要求分散在第12节“特殊成员函数”[special]
中。
答案 1 :(得分:4)
C ++ 03中POD的概念确实过于严格。在C ++ 0x中,POD被推广为包括您描述的对象。所以不用担心,你可以把它命名为POD。在Wikipedia上看到一个不错的总结。
答案 2 :(得分:1)
您的示例的一个问题是它具有隐式声明的,无关紧要的析构函数。尽管有这个名字,但实施并不是AFAIK禁止在非POD类的简单析构函数中做某事。
因此,合法地执行一些奇怪的实现,您的类ex_struct
可能会表现出与以下内容等效的运行时行为:
struct weird_ex_struct
{
int a,b,c,d;
weird_ex_struct() : a(123), aptr(&a) { }
weird_ex_struct(const weird_ex_struct &o) :
a(o.a), b(o.b), c(o.c), d(o.d), aptr(&a) {}
weird_ex_struct &operator=(const weird_ex_struct &o) {
a = o.a; //etc
aptr = &a;
return *this;
}
~weird_ex_struct() {
if (aptr != &a) std::terminate();
}
private:
int *aptr;
}
我说运行时行为,因为weird_ex_struct
有一个非平凡的析构函数,这会影响它如何合法使用(不是在联合中,一方面)。另外我认为有一些标准方法可以在编译时检测私有数据成员的存在。但是只要实现可以保持这个东西是秘密的,除非你做了一些未定义的事情(memcpy
非POD对象),那么之后就可以让你感到惊讶。
显然,如果weird_ex_struct
被memcpy
复制,那么当它被销毁时会发生奇怪的事情。
没有明显的理由要求实现这一点,但标准保留非POD类广泛用于实现奇怪的事情。不确定这是因为他们认为有人会想到一些有用的怪异,或者只是因为他们没有像C ++ 0x那样定义标准布局。
[编辑:约翰内斯指出我对琐碎的析构函数有误 - 由于处理对象生命周期的标准部分中提出的原因,实现不能在依赖于内容的琐碎析构函数中做事。对象的记忆。如果明确地调用析构函数,他们可能会这样做,我不确定。
然而,事实仍然是标准允许实现使用非POD对象做很多疯狂的事情,并且只要你编写构造函数,就打开那扇门。]
答案 3 :(得分:0)
是的,使用memcpy进行复制是安全的,因为构造函数只能初始化值。