【C++进阶】关于POD

关于POD

转自:http://www.cnblogs.com/whyandinside/archive/2012/12/12/2814702.html

plain old data structure (POD) is a data structure that is represented only as passive collections of field values, without using encapsulation or other object-oriented features.

POD是这样的数据结构:用组个field的集合来表示,没有使用封装或者其他面向对象的features。

A POD type in C++ is defined as either a scalar type or a POD class. A POD class has no user-defined copy assignment operator, no user-defined destructor, and no non-static data members that are not themselves PODs. Moreover, a POD class must be an aggregate, meaning it has no user-declared constructors, no private nor protected non-static data, no base classes and no virtual functions. The standard includes statements about how PODs must behave in C++.

In certain contexts, C++ allows only POD types to be used. For example, a union in C++98 cannot contain a class that has virtual functions or nontrivial constructors or destructors. This restriction is imposed because the compiler cannot determine which constructor or destructor should be called for a union. POD types can also be used for interfacing with C, which supports only PODs.

在C++中,我们把传统的C风格的struct叫做POD(Plain Old Data)对象。一般来说,POD对象应该满足如下特性。

对于POD类型T的对象,不管这个对象是否拥有类型T的有效值,如果将该对象的底层字节序列复制到一个字符数组(或者无符号字符数组)中,再将其复制回对象,那么该对象的值与原始值一样。考试就到对于任意的POD类型T,如果两个T指针分别指向两个不同的对象obj1和obj2,如果用memcpy库函数把obj1的值复制到obj2,那么obj2将拥有与obj1相同的值。

简言之,针对POD对象,其二进制内容是可以随便复制的,在任何地方,只要其二进制内容在,就能还原出正确无误的POD对象。对于任何POD对象,都可以使用memset()函数或者其他类似的内存初始化函数。

下面是转自stackoverflow的一篇文章,比较详细介绍了aggregate 和 POD:

 

This article is rather long. If you want to know about both aggregates and POD's (Plain Old Data) take time and read it. If you are interested just in aggregates, read only the first part. If you are interested only in POD's then you must first read the definition, implications, and examples of aggregates and then youmay jump to POD's but I would still recommend reading the first part in its entirety. The notion of aggregates is essential for defining POD's. If you find any errors (even minor, including grammar, stylistics, formatting, syntax, etc.) please leave a comment, I'll edit.

What are aggregates and why they are special

Formal definition from the C++ standard (C++03 8.5.1 §1):

An aggregate is an array or a class (clause 9) with no user-declared constructors (12.1), no private or protected non-static data members (clause 11), no base classes (clause 10), and no virtual functions (10.3).

So, OK, let's parse this definition. First of all, any array is an aggregate. A class can also be an aggrgate if... wait! nothing is said about structs or unions, can't they be aggregates? Yes, they can. In C++ the term class refers to all classes, structs, and unions. So, a class(struct, union) is an aggregate if and only if it satisfies the criteria from the above definitions. What do these criteria imply?

  • This does not mean an aggregate class cannot have constructors, in fact it can have a default constructor and/or a copy constructor as long as they are implicitly declared by the compiler, and not explicitly by the user

  • No private or protected non-static data members. You can have as many private and protected member functions (but not constructors) as well as as many private or protected static data members and member functions as you like and not violate the rules for aggregate classes

  • An aggregate class can have a user-declared/user-defined copy-assignment operator and/or destructor

  • An array is an aggregate even it is an array of non-aggregate class type.

Now it's time to take a look at some examples:

class NotAggregate1
{
 virtual void f(){} //remember? no virtual functions
};

class NotAggregate2
{
 int x; //x is private by default and non-static 
};

class NotAggregate3
{
public:
    NotAggregate3(int) {} //oops, user-defined constructor
};

class Aggregate1
{
public:
    NotAggregate1 member1;   //ok, public member
    Aggregate1& operator = (Aggregate1 const & rhs) {/* */} //ok, copy-assignment  
private:
   void f() {} // ok, just a private function

};

You get the idea. Now it's time to see how aggregates are special. They, unlike non-aggregate classes, can be initialized with curly braces {}. This initialization syntax is commonly known for arrays, and we just learnt that these are aggregates. So, let's start with them.

Type array_name[n] = {a1, a2, ..., am};

if(m == n)
the ith element of the array is initialized with ai
else if(m < n)
the first m elements of the array are initialized with a1, a2, ..., am and the other n - m elements are, if possible, value-initialized (see below for the explanation of the term)
else if(m > n)
the compiler will issue an error
else (this is the case when n isn't specified at all like int a[] = {1,2,3};)
the size of the array (n) is assumed to be equal to m, so int a[] = {1,2,3} is equivalent to int a[3] = {1,2,3};

When an object of scalar type (boolintchardouble, pointers etc.) is value-initialized it means it is initialized with 0 (false for bool0.0 for double etc.). When an object of class type with a user-declared default constructor is value-initialized its default constructor is called. If the default constructor is implicitly defined then all nonstatic members are recursively value-initialized. This definition is imprecise and a bit incorrect but it should give you the basic idea. I may write another faq on differences between zero-initializationvalue-initialization and default-initialization in the near future. A reference cannot be value-initialized. Value-initialization for a non-aggregate class can fail if, for example, the class has no appropriate default constructor.

Examples of array initialization:

class A()
{
   A(int){} //no default constructor
};
class B()
{
   B() {} //default constructor available
};
int main()
{
  A a1[3] = {A(2), A(1), A(14)}; //OK n == m
  A a2[3] = {A(2)}; //ERROR A has no default constructor. Unable to value-initialize a2[1] and a2[2]
  B b1[3] = {B()}; //OK b1[1] and b1[2] are value initialized, in this case with the default-ctor
  int Array1[1000] = {0}; //All elements are initialized with 0;
  int Array2[1000] = {1}; //Attention: only the first element is 1, the rest are 0;
  bool Array3[1000] = {}; //the braces can be empty too. All elements initialized with false
  int Array4[1000]; //no initializer. This is different from an empty {} initializer in that
  //the elements in this case are not value-initialized, but have indeterminate values 
  //(unless, of course, Array4 is a global array)
  int array[2] = {1,2,3,4}; //ERROR, too many initializers

Now let's see how aggregate classes can be initialized with braces. Pretty much the same way. Instead of the array elements we will initialize the non-static data members in the order of their appearance in the class definition (they are all public by definition). If there are fewer initializers than members, the rest are value-initialized. If it is impossible to value-initialize one of the members which were not explicitly initialized, we get a compile-time error. If there are more initializers than necessary, we get a compile-time error as well.

struct X{
    int i1;
 int i2;
};
struct Y{
    char c;
 X x;
 int i[2];
    float f; 
protected:
 static double d;
private:
    void g(){}      
}; 

Y y = {'a', {10,20}, {20,30}};

In the above example y.c is initialized with 'a', y.x.i1 with 10, y.x.i2 with 20, y.i[0] with 20, y.i[1] with 30 and y.f is value-initialized, that is, initialized with 0.0. The protected static member d is not initialized at all, because it is static.

Aggregate unions are different in that you may initialize only their first member with braces. I think that if you are advanced enough in C++ to even consider using unions (their use may be very dangerous and must be thought of carefully), you could look up the rules for unions in the standard yourself :).

Now that we know what's special about aggregates let's try to understand the restrictions on classes, that is, why they are there. We should understand that memberwise initialization with braces implies that the class is nothing more than the sum of its members. If a user-defined constructor is present, it means that the user needs to do some extra work to initialize the members therefore brace initialization would be incorrect. If virtual functions are present, it means that the class has (on most implementations) a pointer to the so-called vtable of the class, which is set in the constructor, so brace-initialization would be insufficient. You could figure out the rest of the restrictions in a similar manner as an exercise :)

So much about the aggregates. Now we can define a stricter set of types, to wit, POD's

What are POD's and why they are special

Formal definition from the C++ standard (C++03 9 §4):

A POD-struct is an aggregate class that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-defined copy assignment operator and no user-defined destructor. Similarly, a POD-union is an aggregate union that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-defined copy assignment operator and no user-defined destructor. A POD class is a class that is either a POD-struct or a POD-union.

Wow, this one's tougher to parse, isn't it? :) Let's leave unions out (on the same grounds as above) and rephrase in a bit clearer way

An aggregate class is called a POD if it has no user-defined copy-assignment operator and destructor and none of its nonstatic members is a non-POD class, array of non-POD, or a reference.

What does this definition imply? (did I mention POD stands for Plain Old Data ?:)

  • All POD classes are aggregates, or, to put it the other way around, if a class is not an aggregate then it is sure not a POD
  • classes, just like structs can be PODs even though the standard term is POD-struct for both cases
  • just like in case of aggregates, it doesn't matter what static members the class has

Examples:

struct POD
{
 int x;
 char y;
 void f() {} //no harm if there's a function
 static std::vector<char> v; //static members do not matter
};

struct AggregateButNotPOD1
{
 int x;
 ~AggregateButNotPOD1(){} //user-defined destructor
};

struct AggregateButNotPOD2
{
 AggregateButNotPOD1 arrOfNonPod[3]; //array of non-POD class
};

POD-classes, POD-unions, scalar types, and arrays of such types are collectively called POD-types.
POD's are special in an extremely many ways. I'll provide just some examples.

  • POD-classes are the closest to C structs. Unlike them, POD's can have member functions and arbitrary static members, but neither of these two changes the memory layout of the object. So if you want to write a more or less portable dynamic library which can be used from C and even .NET you should try to make all your exported functions take and return only parameters of POD-types

  • The lifetime of objects of non-POD class type begins when the constructor has finished and ends when the destructor has finished. For POD classes, the lifetime begins when storage for the object is occupied and finishes when that storage is released or reused.

  • For objects of POD types it is guaranteed by the standard that when you memcpy the contents of your object into an array of char or unsigned char, and then memcpy the contents back into your object, the object will hold its original value. Do note that there is no such guarantee for objects non-POD types. Also, you can safely copy POD objects with memcpy. The following example assumes T is a POD-type

    #define N sizeof(T)
    char buf[N];
    T obj; // obj initialized to its original value
    memcpy(buf, &obj, N); // between these two calls to memcpy,
    // obj might be modified
    memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar type
    // holds its original value
  • goto statement. As you may know, it is illegal (the compiler should issue an error) to make a jump via goto from a point where some variable was not yet in scope to a point where it is already in scope. This restriction applies only if the variable is of non-POD type. In the following example f()is ill-formed whereas g() is well-formed. Note that Microsoft compiler is too liberal with this rule - just issues a warning in both cases.

    int f() {
      struct NonPOD { NonPOD(){}};
      goto label;
      NonPOD x;
    label:
      return 0;
    }
    
    int g(){
      struct POD {int i;  char c;};
      goto label;
      POD x;
    label:
      return 0;
    }
  • It is guaranteed that there will be no padding in the beginning of a POD object. In other words if a POD-class A's first member is of type T you can safely reinterpret_cast from A* to T* and get the pointer to the first member and vice versa.

The list goes on and on...

Conclusion

It is important to understand what exactly a POD is because many language features, as you see, behave differently for them. I hope this was interesting and useful to read.

 

Reference:

1. http://stackoverflow.com/questions/4178175/what-are-aggregates-and-pods-and-how-why-are-they-special/4178176#4178176

2. http://stackoverflow.com/questions/4178175/what-are-aggregates-and-pods-and-how-why-are-they-special/7189821#7189821

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值