C ++ object model-copying constructor construction operations, object model constructor

Source: Internet
Author: User

C ++ object model-copying constructor construction operations, object model constructor
The copy constructor is used to construct a new object based on an existing object.
1. When a constructor is called, there are three situations where the content of one object is used as the initial values of another class object to construct an object, which are: 1) initialize an object, such as class X {...}; X x; X xx = x; // or X xx (x); 2) when an object is passed as a parameter to a function, 3) when the function returns a Class Object
2. Default member copy initialization if the class does not provide an explicit copy constructor, when the class object is constructed with another object as the initial value, this is done internally: For member variables of basic types (such as int and array), bitwise replication is used to copy one object to another; for member variables of the class type, the copy constructor is called recursively.
3. When the copy constructor is merged by the compiler when it is necessary to copy the constructor, it will be constructed by the compiler. What is necessary? This means that when the class does not display the so-called bitwise copy semantics (meaning of one-bit replication.
Like the default constructor, if the class does not declare a replication constructor, an implicitly declared or implicitly defined replication constructor will appear. The replication constructor can be divided into trivial (useless) and nontrivial (useful. Only the copy constructor of nontrivial can be merged by the compiler. The criterion for determining whether a replication constructor is trivial is whether the class shows bitwise copy semantics.
What is bitwise copy semantics.
4. bitwise copy semantics (bit-by-bit replication) in the following code snippet:

class Person{    public:        Person(const char *name, int age);    private:        char *mName;        int mAge;};int main(){    Person p1("p1", "20");    Person p2(p1);    return 0;}

In the above code snippet, p2 should be initialized according to p1. The class Person does not define the replication constructor. According to the class Person definition, all its member variables are basic type variables (pointers and int), and there is no member variable of the class type, no virtual function is defined, and it is not a derived class of a class. In this case, the replication construction operation can be completed through the one-by-one replication (also the default replication operation. In this case, the definition of this class shows bitwise copy semantics, so it does not combine into a copy constructor.
Next we will discuss under what circumstances the class shows non-bitwise copy semantics.
5. bitwise copy semantics is not displayed in the class under the four conditions of non-bitwise copy semantics. It is listed in detail below.
1) when the class contains a member object, and the class of this object declares a copy constructor (whether explicitly declared or compiled or merged) when the Person class is defined as follows:
class Person{    public:        Person(const String &name, int age);    private:        String mName;        int mAge;};class String{    public:        String(const char *str)        String(const String &rhs);    private:        char *mStr;        int mLen;};

Because the Person class does not explicitly define a replication constructor, but it contains a member object (mStr), and the class (String) to which the object belongs defines a replication constructor, so at this time, the Person shows non-bitwise copy semantics. The compiler will synthesize a copy constructor for it. The copy constructor calls the String copy constructor to initialize the member variable mStr, in addition, the initialization of other basic types of member variables (mAge) is completed by means of step-by-step replication.
2) When the class inherits from a base class and the base class has a copy constructor (whether explicitly declared or compiled or merged), for example, the following code snippet:
class Student : public Person{    public:        Student(const String &name, int age, int no);    private:        int mNo;};

As mentioned above, the class Person has a copy constructor synthesized by the compiler because there is a member variable of the String type. Class Student inherits from class Person, so in this case, class Student shows non-bitwise copy semantics. The compiler will synthesize a copy constructor for it. The copy constructor calls the copy constructor of its base class to initialize the base class, and then initializes the member variables of its derived class.
3) when the class declares one or more virtual functions, when the class declares a virtual function, the compiler supports the virtual function mechanism. during compilation, the following operations are performed: 1. add a virtual function table (vtbl) containing the address of each functional virtual function. 2. Install a vptr pointer to the virtual function table of the class in each object of the class ).
To correctly implement the virtual function mechanism, the compiler must set the initial value for the vptr of each newly generated class object. Therefore, the compiler must synthesize a copy constructor to correctly initialize the vptr.
Add a virtual function print to class Person and class Student, as shown below:
class Person{    public:        Person(const char *name, int age);        virtual ~Person();        virtual void print();    private:        const char *mName;        int mAge;};class Student : public Person{    public:        Student(const char *name, int age, int no);        virtual ~Student();        virtual void print();    private:        int mNo;};

Consider the following code:
Student s1 ("s1", 22,100 1); Student s2 = s1; // comment 1 Person p1 = s1; // comment 2

When an object of a class is constructed with another object of the class as the initial value, because the vptr of both objects should point to the virtual function table of the class, it is safe to copy the vptr of another object to the vptr of this object. In this case, bitwise copy semantics can be used. For example, in the above Code, Note 1 corresponds to this situation.
However, when a class object is constructed with the object of its derived class as the initial value, directly copying the vptr value of the derived class object to the vptr of the base class object will cause a major error. For example, in the above Code, Note 2 corresponds to this situation. In this case, the compiler must explicitly set the vptr of the Class Object to point to the virtual function table of the class for the copy constructor synthesized by a class, instead of directly copying the vptr value of its derived class object, and correctly copying the members of the initialization object according to the type of the class.
In general, the copy constructor synthesized by the compiler correctly sets the vptr pointer of the object based on the object type.
4) when the class is derived from an inherited string, one or more virtual base classes must be processed. When a class object is constructed with another object as the initial value, and the latter has a virtual base class sub-object, bitwise copy semantics will become invalid. Each compiler will make the location of the virtual base class sub-object in the derived class object ready during execution (for example, G ++ places the virtual base class sub-object at the end of the derived class Object ), bitwise copy semantics may damage this location, so the compiler must make a judgment in the copy constructor it has synthesized.
For example, in the following code:
class Base{    public:        Base(){mBase = 100;}        virtual ~Base(){}        virtual void print(){}        int mBase;};class VBase : virtual public Base{    public:        VBase(){mVBase = 101;}        virtual ~VBase(){}        virtual void print(){}        int mVBase;};class Derived : public VBase{    public:        Derived(){mDerived = 102;}        virtual ~Derived(){}        virtual void print(){}        int mDerived;};

Consider the following code:
VBase vb1;VBase vb2 = vb1;

As discussed in 3rd), if the object of a class is constructed with another object of the class as the initial value, bitwise copy semantics can be used to complete related operations. The problem still occurs when an object of A derived class is initialized as the initial value of its base class object.
Consider the following code:
Derived d;VBase vb = d;

In this case, in order to completely and correctly set the initial value of vb, the compiler must synthesize a copy constructor and install some code, to complete the initialization of some member variables of the base class Object Based on the object of the derived class, and correctly set the vptr value of the base class.
Taking g ++ as an example, the memory distribution of objects in the Derived class is roughly as follows:
The memory distribution of VBase-like objects is roughly as follows:
From the memory structure of the Derived class and VBase class, we can easily see that bitwise copy semantics cannot be used to construct a base class object with the object of a Derived class as the initial value. Compile and synthesize the copy constructor to copy the member variable (mVBase) in the base class sub-object of the Derived object d to the member variable corresponding to the vb class VBase object, copy the member variable (mBase) in the virtual base class sub-object of Object d to the member variable corresponding to the object vb (that is, copy the yellow part in the initialization diagram ). Finally, set the two vptr of the object vb to point to the correct position.
Note: The two vptr of the Derived class and the two vptr of the VBase class are not the same, and they are also different from the vptr of the Base class.
Use the following code to traverse objects of more than three classes: Note: runtime environment: 32-bit Ubuntu 14.04, g ++ 4.8.2
int main(){    Derived d;    VBase vb = d;    int *p = (int*)&d;    for (int i = 0; i < sizeof(d) / sizeof(int); ++i)    {        cout << *p << endl;        ++p;    }    p = (int*)&vb;    cout << endl;    for (int i = 0; i < sizeof(vb) / sizeof(int); ++i)    {        cout << *p << endl;        ++p;    }    Base b;    cout << endl;    p = (int*)&b;    for (int i = 0; i < sizeof(b) / sizeof(int); ++i)    {        cout << *p << endl;        ++p;    }    return 0;}

The output is shown in:

From the running results, we can see that all vptr of the three classes are different.

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.