Preface
Programmers familiar with C know the usage of Union. Using union, they can use the same bucket to store different types of data, thus saving memory space. You can use "." and "->" to directly access members in the bucket. After C ++ appears, it inherits union and retains its features in C. However, the Union in C ++ has new extensions, which requires everyone to understand. Otherwise, you will be confused and confused. Let me talk about two points.
1. Store objects in Union
In C, union can store any type of built-in data. Can Union store objects in C ++? Let's take a look at an example. It can be explained more than any other words, isn't it?
# Pragma warning (Disable: 4786)
# Include
Using namespace STD;
Class testunionclass
{
Public:
Testunionclass (long l): Data _ (l)
{
};
Int data _;
};
Typedef union _ tagutype _
{
Testunionclass OBJ;
} Ut;
Int main (void)
{
Return 0;
}
This is not the case. In Union, testunionclass objects cannot be stored, but in C, union can be used to store struct objects. Why cannot we store class objects? It's easy. Can Union store struct with constructors in C? By the way, there is no constructor in struct in C. So if Union in C ++ can store objects of classes with constructors, it is not logical. Isn't c ++ fully compatible with C? Yes. Because of this, union in C ++ cannot store class objects with constructor, but it can store objects of classes without constructor, this is the same as C. I don't want to believe it. Modify the declaration of the testunionclass class as follows:
Class testunionclass
{
Public:
Int data _;
};
Compile again, everything is OK !. However, in this case, the initialization feature of C ++ is lost. It makes no sense to do so. I am just talking about its semantics in C ++, it is not recommended for use (absolutely not recommended ). However, we can store object pointers in union to reference different object types. I don't need to talk about it anymore. Let's try it!
Ii. Union initialization in the class
Due to the shared memory feature of union, we can make our class store different types without wasting memory space. In the class, we can declare a union to store different types of pointers, for example:
# Pragma warning (Disable: 4786)
# Include
Using namespace STD;
Class testunionclass
{
Enum storetype {long, const_charp };
Union
{
Const char * CH _;
Long L _;
} Data _;
Storetype stype _;
Testunionclass (testunionclass &);
Testunionclass & operator = (const testunionclass &);
Public:
Testunionclass (const char * Ch );
Testunionclass (long L );
Operator const char * () const {return data _. CH _;}
Operator long () const {return data _. L _;}
};
Testunionclass: testunionclass (const char * Ch): Data _. CH _ (CH), stype _ (const_charp)
{
}
Testunionclass: testunionclass (long l): Data _. L _ (L), stype _ (long)
{
}
Int main (void)
{
Testunionclass pszobj ("Yuankai ");
Testunionclass lobj (1234 );
Cout <(pszobj) <lobj <Endl;
Return 0;
}
Unfortunately, compilation fails. It seems that there is no problem. Why? Is there a problem with data _. CH _ (CH) and Data _. L _ (l? If you ask a C programmer, he will tell you that there is absolutely no problem. You won't doubt about the compiler! Sorry! That's what I thought at the beginning. Confusing. Let's take a look at what happened to construct the testunionclass object, so that you will understand. When creating a testunionclass object, you must call its corresponding constructor. In constructor, you must call the constructor of its members. Therefore, you must call the constructor of the union member, but it is anonymous and no constructor can be called, so an error occurs. Obviously, in C ++, union can have constructor like class, and its members cannot be referenced directly. Struct also has this restriction. As long as we define a constructor for it, all problems are solved. Example:
Class testunionclass
{
Enum storetype {long, const_charp };
Union dataunion // Anonymous
{
Dataunion (const char *); // declare the const char * const
Dataunion (long); // declare the long Constructor
Const char * CH _;
Long L _;
} Data _;
Storetype stype _;
Testunionclass (testunionclass &);
Testunionclass & operator = (const testunionclass &);
Public:
Testunionclass (const char * Ch );
Testunionclass (long L );
Operator const char * () const {return data _. CH _;}
Operator long () const {return data _. L _;}
};
Testunionclass: testunionclass (const char * Ch): Data _ (CH), stype _ (const_charp)
{// Pay attention to data _ (CH). Here we directly reference data _
}
Testunionclass: testunionclass (long l): Data _ (L), stype _ (long)
{// Note data _ (L). Data _ (l) is directly referenced here _
}
Testunionclass: dataunion (const char * Ch): CH _ (CH)
{
}
Testunionclass: dataunion (long l): L _ (l)
{
}
Compile again now. If not, you suspect that the compiler has a problem for a reason.
C. Because there is no concept of class, all types can be considered as a combination of basic types, it is natural to include struct in union, after C ++, since it is generally believed that struct in C ++ is basically equivalent to class, can there be class members in union? Let's take a look at the following code:
Struct teststruct
{
Teststruct (){}
};
Typedef Union
{
Teststruct OBJ;
} Ut;
Int main (void)
{
Return 0;
}
Compile the program and we will be notified:
Error c2620: Union '_ unnamed': Member 'obj 'has user-defined constructor or non-trivial default constructor
If the constructor with nothing to do is removed, everything is OK.
Why does the compiler not allow our union members to have Constructors? I cannot find an authoritative explanation of this issue. For this question, my explanation is:
If the C ++ standard allows our Union to have constructors, do we need to execute this constructor during space allocation? If the answer is yes, if the teststruct constructor contains some memory allocation operations or other modifications to the entire application state, if I want to use OBJ in the future, it may be reasonable, but what if I don't use the OBJ member at all? The modification to the system status caused by the introduction of obj is obviously unreasonable; otherwise, if the answer is no, once we select OBJ for future operations, all information is not initialized (if it is a normal struct, there is no problem, but what if there is a virtual function ?). Furthermore, let's assume that our Union is not only one teststruct OBJ, but also one teststruct2 obj2. Both of them have constructors, in addition, some memory allocation tasks are executed in the constructor (many other tasks are done). If obj is constructed first, and obj2 is constructed later, then the execution result will almost certainly cause memory leakage.
In view of the above troubles (there may be more trouble), when constructing a union, the compiler is only responsible for allocating space and not executing additional initialization tasks. In order to simplify the work, as long as the constructor is provided, the above error will be received.
Likewise, except constructors, destructor, copy constructors, and value assignment operators cannot be added.
In addition, if our class contains any virtual functions, we will receive the following error message during compilation:
Error c2621: Union '_ unnamed': Member 'obj 'has copy constructor
Therefore, the idea that Union contains class member variables with Constructors/destructor/copy constructors/assign value operators/virtual functions is dispelled, be honest with your C-style struct!
However, defining a common member function is OK, because it does not make any essential difference between the class and the C-style struct, you can fully understand this class as a C-style struct + N global functions.
Now let's look at the differences when the class contains internal union. Take a look at the following procedures and read the program prompts:
Class teststruct
{
Union dataunion
{
Dataunion (const char *);
Dataunion (long );
Const char * CH _;
Long L _;
} Data _;
Public:
Teststruct (const char * Ch );
Teststruct (long L );
};
Teststruct: teststruct (const char * Ch): Data _ (CH) // if you want to use initialing list to initiate a nested-union member, the Union must not be anonymous and must have a constructor.
{
}
Teststruct: teststruct (long l): Data _ (l)
{
}
Teststruct: dataunion (const char * Ch): CH _ (CH)
{
}
Teststruct: dataunion (long l): L _ (l)
{
}
Int main (void)
{
Return 0;
}
As shown in the above program, union in C ++ can also contain constructors. However, although this is supported by languages, it is really a bad programming habit. Therefore, I don't want to explain too much about the above program. I recommend the following programming style:
Class teststruct
{
Union dataunion
{
Const char * CH _;
Long L _;
} Data _;
Public:
Teststruct (const char * Ch );
Teststruct (long L );
};
Teststruct: teststruct (const char * Ch)
{
Data _. CH _ = CH;
}
Teststruct: teststruct (long l)
{
Data _. L _ = L;
}
Int main (void)
{
Return 0;
}
It is completely C-style.
Iii. Union can avoid C ++ type checks
Read fastdelegate. h and see this piece of code.
Template <class outputclass, class inputclass>
Union horrible_union
{
Outputclass out;
Inputclass in;
};
Template <class outputclass, class inputclass>
Inline outputclass horrible_cast (const inputclass input)
{
Horrible_uion <outputclass, inputclass> U;
Typedef int error_cantuserhorrible_cast [sizeof (inputclass) = zizeof (u) & sizeof (inputclass) = sizeof (outputclass )? 1:-1];
U. In = input;
Return U. out;
};
Here (forcibly convert input to output type), a union is used in the function to avoid the C ++ type check.
Iv. How to use anonymous union in C and C ++
Main ()
{
Union
{/* Define a Union */
Int I;
Struct
{/* Define a structure in the Union */
Char first;
Char second;
} Half;
};
I = 0x4241;/* assignment of union members */
Half. First = 'a';/* assign values to structure members in the Union */
Half. Second = 'B ';
Printf ("% x/N", I );
Getchar ();
}
In the above example, if it is a C program, it cannot pass, and the error: I undeclared identifier; half undeclared identifier is prompted.
However, if you save this example as a C ++ file, it can be compiled and run normally.
Anonymous union only notifies the compiler that its member variables share an address. The variables themselves are directly referenced and do not use the common dot operator syntax. For example:
# Include <iostream>
Void main ()
{
Union
{
Int test;
Char C;
};
Test = 5;
C = 'a ';
STD: cout <I <"" <C;
}
As we can see, the Federated component is referenced as a declared common local variable. In fact, for programs, this is exactly how these variables are used. in addition, although defined in a joint declaration, they have the same scope level as any other local variable in the same program. this means that the name of the member in the anonymous union cannot conflict with any other constant identifier in the same scope.
The following restrictions apply to anonymous union:
Because the vertex operator is not used for anonymous union, the elements contained in the anonymous union must be data, and member functions are not allowed or private or protected members are not allowed. In addition, the global anonymous union must be static. Otherwise, it must be placed in an anonymous namespace.
V. space occupation of the union structure in C/C ++
Many books say that the size of memory space occupied by union is the size of space occupied by members who occupy the largest space. This is wrong! How can we fool people like this? Why don't our technicians make things clear?
Let's look at an example:
Struct Block
{
Struct block * next;
Char * avail;
Char * limit;
};
Union align
{
Struct Block B;
Double D;
};
Sizeof (align) =?
As mentioned in the book, the maximum member is struct Block B. It is 12 bytes in size (in windows and VC compiler environments). But is that true? Error!
Sizeof (align) = 16.
Why? You can see the following analysis.
In the preceding example, B occupies 12 bytes and D occupies 8 bytes. If align only allocates 12 bytes of space, so when the double type D is stored, only one half can be stored. This is not acceptable. (what is the case for a half ?)
Therefore, it must be extended by four more bytes. In this way, B can be stored, and D can be an integer multiple;
Therefore, 16 is the last space required.
6. Some points to be discussed:
1. What in the Union cannot be stored?
We know that all the items in the Union share the memory, so static and reference cannot be used, because they cannot share the memory.
2. Can classes be combined?
Let's first look at an example:
Class Test
{
Public:
Test (): Data (0 ){}
PRIVATE:
Int data;
};
Typedef union _ Test
{
Test test; file ://?????? /
} Ui;
But why?
Because consortium cannot store classes with constructors, analysis functions, and copy operators, the compiler cannot guarantee that these objects will not be destroyed because they share the memory, it cannot be guaranteed that the function can be called when it leaves.
3. Why is it anonymous ??
Let's take a look at the next piece of code:
Class Test
{
Public:
Test (const char * P );
Test (INT in );
Const operator char * () const
{
Return data. ch;
}
Operator long () const
{
Return data. L;
}
PRIVATE:
Enum type
{
Int, string
};
Union
{
Const char * Ch;
Int I;
} Datatype;
Type stype;
Test (test &);
Test & operator = (const Test &);
};
Test: Test (const char * P): stype (string), datatype. ch (P)
{}
Test: Test (INT in): stype (INT), datatype. L (I)
{}
Do you see any problems? Haha, compilation and translation fail. Why? Is there a problem with datatype. ch (P) and datatype. L (I?
Haha, where is the problem? Let's take a look at what happened when constructing the test object. when creating the test object, we naturally need to call its corresponding constructor. In the constructor, of course, we need to call the constructor of its members, so he wants to call the constructor of the datatype member, but he does not call the constructor, so an error occurs.
Note that this is not an anonymous union! Because it is followed by a piece of data!
4. How to effectively prevent access errors?
Union can save memory space, but there is also a certain risk: Get the value of the current object through an inappropriate data member! For example, the above CH and I requests are staggered.
To prevent such errors, we must define an additional object to track the value types currently stored in the Union. We call this additional object the 'join' discriminant.
A good experience is to provide a set of access functions for all Union data types when processing Union objects as class members.