The initialization syntax is unified in the C ++ 11 standard. Before learning about these changes, we need to understand the aggregate and POD types, I saw a good article on Stack Overflow (original article). I have a detailed explanation of the changes in aggregate, POD, and C ++ 11. I feel very good. Translate the First Half of the article first, the second half is given in two days.
------------------------------------------------------------------------- Translation
How to read:
This article is a long one. If both aggregates and pods want to know about it, just calm down and read it completely. If you are only interested in aggregates, read the first part. If you are only interested in pods, you must first read the definition, meaning, and examples of aggregates, and then skip to read pods, but I still recommend that you complete the first part. The concept of aggragates is the basis for defining pods.
What is aggragates?Why are they so special?
C ++ standard (C ++ 03 8.5.1§ 1:
An aggregate is an array or a non-static data member with no user declaration, no private or protected type, and no parent class or virtual function type.
Now let's analyze this definition. First, the array is aggregate. Class can also be aggregate... Wait! We haven't mentioned struct and unions. Can they be aggregate? Yes, they can. In C ++, the term class refers to all classes, structs, and unios. Therefore, class (struct, Union) can become aggregate as long as it meets the conditions defined above. What are the meanings of these conditions?
- This does not mean that the aggregate type cannot have constructors. In fact, it can have a default constructor or a replication constructor, as long as they are declared by the compiler, instead of being declared by the user.
- You cannot have private or protected non-static data members. You can define any number of private or protected member methods (excluding constructors) and static data members and methods. This does not violate the rules of the aggregate type.
- The aggregate type can have user-declared/user-defined value assignment operators or destructor.
- An array is of the aggregate type, even an array of non-aggregate elements.
Here are several examples:
1 class NotAggregate1 2 { 3 virtual void f(){} //remember? no virtual functions 4 }; 5 6 class NotAggregate2 7 { 8 int x; //x is private by default and non-static 9 };10 11 class NotAggregate312 {13 public:14 NotAggregate3(int) {} //oops, user-defined constructor15 };16 17 class Aggregate118 {19 public:20 NotAggregate1 member1; //ok, public member21 Aggregate1& operator = (Aggregate1 const & rhs) {/* */} //ok, copy-assignment 22 private:23 void f() {} // ok, just a private function24 25 };
You have understood the meaning of aggregates. Now let's see why it is so special. They are different from non-aggregates types and can be initialized using. This initialization syntax is very common in arrays, And we just learned that the data is of the aggregates type. Therefore, we start from the array:
Type array_name[n] = {
A1, A2,..., AM};
If (M = N)
The I-th element of the array is initialized to AI.
Else if (M <n)
Array front edgeMElements are initializedA1, A2,..., AM, the remaining N-M elements. If possible, initialize by value (the following is an explanation of this term)
Else if (M> N)
Compilation errors may occur.
Else(This form may be used.a[] = {1,2,3};
)
The length of the array is assumed to be m, so int A [] = {1, 2, 3} is equal to a [3] = {1, 2, 3}
A scalar (bool, Int, Char, double, pointer) object is initialized by value (value-initialized, it indicates that it is initialized to 0 (the bool type is initialized to false, and the double type is initialized to 0.0, etc ). When an object of the class type of the default constructor declared by the user is initialized by value, its default constructor will be called. If the default constructor is implicitly defined, all non-static member variables will be recursively initialized by value. Although this definition is not accurate or completely correct, it can give you a basic understanding. I will write an article aboutZero-initialization,Value-initializationAndDefault-initialization. Reference cannot be initialized by value. Value-based initialization for non-aggregate classes may fail, for example, in the absence of a suitable default constructor.
Array initialization example:
1 class A () 2 {3 A (INT) {}// no default constructor 4}; 5 Class B () 6 {7 B () {}// default constructor available 8}; 9 int main () 10 {11 A A1 [3] = {A (2), a (1 ), A (14)}; // OK n = m12 a A2 [3] = {A (2)}; // error A does not have a default constructor. you Cannot initialize A2 [1] and A2 [2] 13 B B1 [3] = {B ()} by value ()}; // OK B1 [1] and B1 [2] use the default constructor to initialize 14 int array1 [1000] = {0} by value }; // all elements are initialized to 015 int array2 [1000] = {1}; // Note: only the first element is initialized to 1, and the others are initialized to 0; 16 bool array3 [1000] ={}; // the braces can be empty. All elements are initialized to false; 17 int array4 [1000]; // not initialized. this is different from null {} initialization. 18 // in this case, elements are not initialized by value. Their values are unknown and uncertain; 19 // (unless array4 is global data) 20 int array [2] = {1, 2, 3, 4}; // error, too many initial values
Now let's take a look at how the aggregates type is initialized using. Similar to the above, non-static member variables are initialized according to the declared order within the class (all definitions must be public. If the initial value is less than the Member value, other members will be initialized by value. If one member cannot perform value-based initialization, we will get a compilation error. If the initial value is more than the Member value, we also get a compilation error.
1 struct X{ 2 int i1; 3 int i2; 4 }; 5 struct Y{ 6 char c; 7 X x; 8 int i[2]; 9 float f; 10 protected:11 static double d;12 private:13 void g(){} 14 }; 15 16 Y y = {'a', {10,20}, {20,30}};
In the above example, Y. C is initialized to 'A', Y. x. i1 is initialized to 10, Y. x. i2 is initialized to 20, Y. I [0] is 20, Y. I [1] is 30, Y. f is initialized by value, that is, it is initialized to 0.0. The static member variable D of the protection type is not initialized because it is static.
The unions of the aggregate type are different. If you use {}, you may only be able to initialize their first member. I think if you use C ++ advanced, consider using unions (it is very dangerous to use them, you must be careful.) You can certainly find the unions rules in the C ++ standard.
We understand the special characteristics of aggregates. Now let's try to understand its type restrictions, that is, why are these restrictions. We should understand that using {} for member initialization means that this type is only a set of members. If there is a user-defined constructor, it means that the user needs to do some extra work to initialize the members, so using {} for initialization is incorrect. If there is a virtual function, it means that this type (most implementations) has a pointer to vtable and needs to be set in the constructor. Therefore, using {} for initialization is not enough. As an exercise, you can understand the meaning of other restrictions in this way.
There are so many aggregates. Now we can strictly define a sub-type pods.
What is pods?Why are they so special?
C ++ standard (C ++ 03 9§ 4) Is formally defined:
The pod-struct type is a non-pod-struct, non-pod-union (or arrays of these types), and a data member of the reference type, there is no user-defined value assignment operator or aggregate class of the destructor. Similarly, pod-union is a non-static non-pod-struct, non-pod-union (or arrays of these types), and a referenced data member, there is no combination of the User-Defined value assignment operator and the aggregate type of the destructor. The pod type is one of pod-struct and a pod-union.
Wow, this definition is more difficult to interpret, isn't it? Let's remove unions and repeat it as follows:
The pod type is a non-pod type (or an array of these types) that does not have a non-static type and a data member of the reference type, there is no user-defined value assignment operator or aggregate type of the destructor.
What does this definition mean? (Pod is plain old data)
- All pod types are of the aggregates type. In other words, if it is not of the aggregate type, it must not be of the pod type.
- Class, which can be of the pod type like struct, because the standard pod-struct term contains the two cases.
- Like the aggregates type, it does not matter what type Static members are.
Example:
1 struct POD 2 { 3 int x; 4 char y; 5 void f() {} //no harm if there's a function 6 static std::vector<char> v; //static members do not matter 7 }; 8 9 struct AggregateButNotPOD110 {11 int x;12 ~AggregateButNotPOD1(){} //user-defined destructor13 };14 15 struct AggregateButNotPOD216 {17 AggregateButNotPOD1 arrOfNonPod[3]; //array of non-POD class18 };
Pod-classes, pod-unions, the scalar type and the number of these types are combined into the pod type, and the pod type is very special in many aspects. I will give a few examples:
- The pod type is closest to the struct type in C. They did not change the memory layout of objects, but the pod type can have its own member functions and any type of static members. Therefore, if you want to write a portable dynamic library that can be used on the C or. NET platform, you should make the returned values and parameters of all exposed methods be of the pod type.
- The life cycle of a non-pod object starts with the constructor and ends with the completion of the Destructor call. The lifecycle of a pod object starts when the space of the storage object is occupied, and ends when the space is released or reused.
- For pod objects, the C ++ standard ensures that when you use memcpy to copy the object content to an array of the char or unsigned char type and use memcpy to copy it back, the object remains unchanged. Note that non-pod types cannot guarantee this. Of course, you can also securely copy the pod type between objects. The following example assumes that t is of the pod type.
1 #define N sizeof(T)2 char buf[N];3 T obj; // obj initialized to its original value4 memcpy(buf, &obj, N); // between these two calls to memcpy,5 // obj might be modified6 memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar type7 // holds its original value
GOTO statement. As you know, it is illegal to use goto to jump from a vertex where the variable is not declared to a vertex where the variable has been declared (the compiler should report an error ). This restriction is only valid for non-pod types. In the following example, F () is invalid, while G () is legal. I noticed that Microsoft's compiler was too generous with this rule and only gave a warning.
1 int f() { 2 struct NonPOD { NonPOD(){}}; 3 goto label; 4 NonPOD x; 5 label: 6 return 0; 7 } 8 9 int g(){10 struct POD {int i; char c;};11 goto label;12 POD x;13 label:14 return 0;15 }
The C ++ standard ensures that pod objects are not cheap at the beginning of the memory. That is to say, if the first member of a pod type A is t, you can safely call reinterpret_cast to convert it from a * to T * to obtain the pointer of the first Member, which is also true in turn.
This list is still very long...
Conclusion
Understanding the pod type is very important, because many c ++ language features, as you can see, are different for them. I hope this article will be useful to you.