C ++ Object Model -- create a Copy Constructor (chapter 2)

Source: Internet
Author: User

C ++ Object Model -- create a Copy Constructor (chapter 2)
2.2 create Copy ConstructorIn three cases, the content of one object is used as the initial value of another class object., The most obvious case is to perform an Explicit initialization operation on an object, for example:

Class X {...}; X x X; // use the content of one object as the initial value of another class object x xx = X;
In the other two cases, when an object is passed to a function as a parameter, for example
Extern void foo (X x); void bar () {X xx; // use xx as the initial value of the first foo () parameter (implicit initialization Operation) foo (xx );}
And when the function returns a class object. For example:
X foo_bar() {    X xx;    return xx;}
Assume that the class designer clearly defines a copy constructor (this is a constructor, and the type of a parameter is its class type). For example:
// User-defined copy constructor instance // it can be in the form of multiple parameters. The second and subsequent parameters are supplied with X: X (const X & x) by a default value ); y: Y (const Y & y, int = 0 );
In most cases, when a class object uses another similar entity as the initial value, the preceding constructor will be called. This may lead to the generation of a temporary class object or the transformation of program code.
Default Memberwise Initialization what if the class does not provide an explicit copy constructor? When the class object uses another object of the same class as the initial value, It is implemented internally by the so-called default memberwise initialization, that is, the value of each built-in or derived data member (such as a pointer or array, copy one copy of an object to another, but it does not copy the member class object, instead implements memberwise initialization recursively.For example, consider the following class declaration:
Class String {public: //... no explicit copy constructorprivate: char * str; int len ;};
The default memberwise initialization of a String object occurs in this case:
String noun (book); String verb = noun; its completion method is the same as setting every members individually: verb. str = noun. str; verb. len = noun. len;
If a String object is declared as a member of another class, it is shown as follows:
Class Word {public: //... no explicit copy constructorprivate: int _ occurs; String _ word; // String object becomes a member of class Word };
The default memberwise initialization of a Word object will copy its built-in member _ occurs, and then recursively implement memberwise initialization from String member object _ word.
How is such an operation actually completed? ARM pointed out:
In terms of concept, for a class X, this operation is implemented by a copy constructor.
The key is that, in terms of concept, this annotation is followed by some explanations:
A good compiler can generate bitwise copies for most class objects because they have bitwise copy semantics...
That is to say, if a class does not define the copy constructor, the compiler will automatically generate this sentence for it. Instead, it should be like ARM:
Default constructors and copy constructors are generated by the compiler when necessary.
This sentence is necessary when the class does not show bitwise copy semantics. C ++ Standard retains the meaning of ARM, but the discussions are more formal as follows:
A class object can be copied in two ways: initialization and assignment ). In terms of concept, the two operations are completed by copy constructor and copy assignment operator respectively.
Like the default constructor, C ++ Standard points out that if the class does not declare a copy constructor, an implicit declaration will appear. C ++ Standard distinguishes copy constructor into trivial and nontrivial. Only nontrivial entities are merged into programs, the criterion for determining whether a copy constructor is trivial is whether the class shows the so-called bitwise copy semantics.

Bitwise Copy Semantics is included in the following program snippet:
#include Word.hWord noun(book);void foo() {    Word verb = noun;}
Obviously, verb is initialized Based on the noun, but it is impossible to predict the program behavior of this initialization operation before the class Word declaration is seen. If the designer of class Word defines a copy constructor, the initialization operation of verb will call it, but if this class does not define the explicit it copy constructor, will there be an entity merged by the compiler called? This depends on whether the class shows bitwise copy semantics. As follows:
// The following statement shows the bit copy semanticsclass Word {public: Word (const char *);~ Word () {delete [] str;} private: int cnt; char * str ;};
In this case, you do not need to synthesize a default copy constructor. Because the above statement shows default copy semantics, a function call is not required for verb initialization. However, if class Word is declared as follows:
// The following statement does not show bitwise copy semanticsclass Word {public: Word (const String &);~ Word (); private: int cnt; String str ;}; Where String declares an explicit copy constructor: class String {public: String (const char *); string (const String &);~ String ();};
In this case, the compiler must synthesize a copy constructor to call the copy constructor of the member class String object:
// A merged copy constructor // C ++ pseudocode inline Word: Word (const Word & wd) {str. string: String (wd. str); cnt = wd. cnt ;}
It is worth noting that in the merged copy constructor, nonclass members, such as integers, pointers, and arrays, are also copied.

Do not Bitwise copy Semantics When does a class not show bitwise copy semantics?There are four situations:
1. When the class contains a member object while the latter class declaration has a copy constructor (whether explicitly declared by the class designer or synthesized by the compiler)
2. When the class inherits from a base class and the latter has a copy constructor
3. When the class declares one or more virtual functions
4. When the class is derived from an inherited string chain, one or more virtual base classe
In the first two cases, the compiler must insert the copy constructors call operation of members or base class into the merged copy constructor. The last two cases are a bit complicated, as described in the following section.
Reset the Virtual Table pointer Two program expansion operations during compilation (as long as one class declares one or more virtual functions):
Add a virtual function table (vtbl) containing the address of each virtual function.
Insert a pointer (vptr) to the virtual funtcion table to each class object.
Obviously, if the compiler fails to correctly set the initial value for the vptr of each new class object, it will lead to terrible consequences. Therefore, when the compiler imports a vptr to the class, the class will no longer display bitwise semantics. Now, the compiler needs to synthesize a copy constructor so that the vptr can be properly initialized, as shown below:
First, define two classes, ZooAnimal and Bear
Class ZooAnimal {public: ZooAnimal (); virtual ~ ZooAnimal (); virtual void animate (); virtual void draw (); private: // required data of ZooAnimal animate () and draw () //}; class Bear: public ZooAnimal {public: Bear (); void animate (); void draw (); virtual void dance (); private: // Bear's animate () and draw () and dance () // required data };
ZooAnimal class object uses another ZooAnimal class object as the initial value, or Bear class object uses another Bear class object as the initial value, which can be directly completed by bitwise copy semantics. For example:
Bear yogi;Bear winnie = yogi;
Yogi is initialized by default Bear constructor. In constructor, The vtpr of yogi is set to point to the virtual table of Bear class. Therefore, copying the vptr value of yogi to winnie's vptr is complete.
When a base class object is initialized with its derived class content, its vptr copy operation must also be secure. For example:
ZooAnimal franny = yogi; // sliced)
Franny's vptr cannot be set to point to the virtual table of Bear class. Otherwise, when draw () in the following program fragment is called and franny is passed in, it will blow up (blow up)
Void draw (const ZooAnimal & zoey) {zoey. draw ();} void foo () {// franny's vptr points to ZooAnimal's virtual table // instead of Bear's virtual table ZooAniaml franny = yogi; draw (yogi ); // call Bear: draw () draw (franny); // call ZooAnimal: draw ()}
Call virtual function draw () through franny to call It uses the ZooAnimal object instead of the Bear object (although franny uses the Bear object yogi as the initial value ). Because franny is a ZooAnimal object. In fact, the Bear part of yogi has been cut (sliced) during franny initialization ). If franny is declared as a reference (or if it is a pointer and its value is the address of yogi), draw () called by franny () is the function entity of Bear.
The merged ZooAnimal copy constructor explicitly sets the vptr of the object to point to the virtual table of the ZooAnimal class, instead of directly copying the vptr value from the class object.

Processing the existence of Virtual Base Class SubobjectVirtual base class requires special processing. If one class object uses another object as the initial value while the latter has a virtual base class subobject, bitwise copy semantics will also become invalid.
Each editor's Support Commitment to virtual inheritance means that the virtual base class subobject position in the derived class object must be ready during the execution period. It is the editor's responsibility to maintain the integrity of the location. Bitwise copy semantics may damage this location, so the editor must make arbitration in its own merged copy constructor.For example, in the following declaration, ZooAnimal becomes a virtual base class of Raccon:
Class Raccon: public virtual ZooAnimal {public: Raccon () {/* set the initial value of private data */} Racccon (int val) {/* set the initial value of private data */}//... private: // required data };
The code generated by the compiler (used to call ZooAnimal's default constructor, initialize Racccon's vptr, and locate ZooAnimal subject in Raccon) is inserted between two Raccon constructors.
What about memberwise initialization? The existence of a virtual base class will invalidate bitwise copy semantics. Second, the problem does not occur when a class object uses another similar object as the initial value, but when a class object uses an object of Its derived classes as the initial value. for example, let Racccon object use a RedPanda object as the initial value, and RedPanda declares the following:
Class RedPanda: public Raccon {public: RedPanda () {/* set the initial value of private data */} RedPanda (int val) {/* set the initial value of private data */} private: //...};
If one Reccon object is used as the initial value of another Raccon object, bitwise copy has more timestamps.
// Simple bitwise copy is enough for Raccon rocky; Raccon little_critter = rocky;
However If you attempt to use a RedPanda object as the initial value of little_critter, the compiler must determine whether the programmer can correctly execute it when attempting to access its ZooAnimal subobject
// Simple bitwise copy is not enough // the compiler must explicitly initialize RedPanda little_red for litte_critter's virtual base class pointer/offset; Raccon little_critter = little_red;
In this case, To complete the correct Initial Value Setting for little_critter, the compiler must synthesize a copy constructor and insert some codes to set the initial values of virtual base class pointer/offset, and perform necessary initial values for each members, and other memory-related operations(3.4 for more details about virtual base classes)
In the following cases, the compiler cannot know whether bitwise copy semantics is still maintained, because it cannot know whether the Raccon Pointer Points to a real Raccon object or a derived class object:
// Simple bitwise copy may be enough, and Raccon * ptr may not be enough; Raccon little_critter = * ptr;
When an initialization operation exists and maintains the bitwise copy semantics State, if the compiler can ensure that the object has a correct and equal initialization operation, should it suppress the call of copy constructor, to optimize the program code?
At least under the merged copy constructor, the possibility of program side effects is zero, so the optimization seems reasonable. What if copy constructor is provided by the class designer? This is a controversial issue.
In the four cases described above, the class no longer maintains bitwise copy semantics, and the default copy constructor is considered nontrivial if it is not declared. In these four cases, in the absence of a declared copy constructor, the compiler must synthesize a copy constructor to correctly process a class object as the initial value of another class object. The next section describes how the compiler calls ocpy constructor policies and how these policies affect the program.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.