Deep Exploration C ++ object model Reading Notes (2 ).
The default constructor is merged only when the compiler needs it.
Generally, the default constructor synthesized by the compiler is useless (trivial), but there are several exceptions:
(1) member Class Object with "default constructor"
If a class does not have any constructor, but it contains a member object, the latter has default constructor, then the compiler will synthesize a default constructor named "nontrivial" without this class when the constructor really needs to be called. to avoid merging multiple default constructor, the solution is to merge the default constructor, copy constructor, destructor, and assignment copy operator in the inline mode. An inline function has a static link and will not be seen by anyone other than the file. If the function is too complex to be made into inline, an explicit non-inline static object will be merged.
According to the principle, "If Class A contains one or more member class objects, each constructor of Class A must call the default constructor of each member classes ", the compiler expands a user-defined default constructor, insert the default constructor associated with each member according to the "Declaration Order of member objects in class" before the explicit user code.
class Dcpey { public:Dopey(); ... };
class Sneezy { public:Sneezy(int); Sneezy(); ... };
class Bashful { public:Bashful(); ... };
class Snow_White {
public:
Dopey dopey;
Sneezy sneezy;
Bashful bashful;
// ...
private:
int mumble;
};
Snow_white: snow_white (): Sneezy (1024)
{
Mumble = 2048;
}
// Default constructor after compiler Expansion
Snow_white: snow_white (): Sneezy (1024)
{
// Insert a member Class Object
// Call its constructor
Dopey. Dopey: dopey ();
Sneezy. Sneezy: Sneezy ();
Bashful. bashful: bashful ();
// explicit user code
mumble = 2048;
}
Keywords: malloc wxWidgets OpenGL polymorphism doxygen Deep Exploration C ++ object model Reading Notes (2 ).
(2) base class with default constructor
If a class without any constructors is derived from a base class with a "default constructor", the default constructor of the derived class will be considered nontrivial and therefore need to be synthesized.
Note that the compiler inserts these base class constructor before the member object.
(3) class with a virtual function
In two other cases, you also need to synthesize the default constructor:
(A) Class declares (or inherits) a virtual function
(B) Class is derived from an inherited string, with one or more virtual base classes
The following program snippet is used as an example:
class Widge {
public:
virtual void flip() = 0;
// ...
};
void flip(const Widge& widge) { widge.flip(); }
// Assume that both Bell and whistle are derived from widge.
Void Foo ()
{
Bell B;
Whistle W;
flip(b);
flip(w);
}
The following two expansion operations will occur during compilation:
1. A virtual function table is generated by the compiler and the virtual functions address of the class is put in it;
2. In each class object, an extra pointer member (that is, vptr) is synthesized by the compiler and contains the relevant class vtbl address.
In addition, virtual invocation of widge. Flip () is rewritten to use the vptr of widge and the flip () entry in vtbl.
// Virtual Operation transformation caused by widge. Flip ()
(* Widge. vptr [1]) (& widge)
For this mechanism to take effect, the compiler must set an initial value for the vptr of each widge (or its derived class) object and place the appropriate virtual table address.
Keywords: malloc wxWidgets OpenGL polymorphism doxygen Deep Exploration C ++ object model Reading Notes (2 ).
(4) class with a virtual base class
class X { public: int i; };
class A : public virtual X { public: int j; };
class B : public virtual X { public: double d; };
class C : public A , public B{ public: int k; };
// The position of PA-> X: I cannot be determined during compilation.
Void Foo (const A * pA) {pa-> I = 1024 ;}
main()
{
foo(new A);
foo(new C);
// ...
}
The compiler needs to insert a pointer to each virtual base classes in the derived class object so that all operations "access a virtual base class through reference or pointer" can be completed through related pointers.
// Possible compiler transformation operations
// _ Vbcx indicates the pointer generated by the compiler, pointing to virtual base class X
Void Foo (const A * pA)... {pa-> _ vbcx-> I = 1024 ;}
Summary: in the merged default constructor, only base class subobjects and member class objects will be initialized, and all other nonstatic data member will not be initialized.
In three cases, the content of one object is used as the initial value of another class object, that is, object Value assignment, object parameter passing, and object as the function return value.
If the class does not provide an explicit copy constructor, It is internally implemented by the so-called default memberwise initialization method, that is, every built-in or derived data member (such as a pointer or an array) to copy one copy of an object to another, but it does not copy the member Class Object, instead implements memberwise initialization recursively. copy constructor is generated by the compiler only when necessary (the class does not show bitwise copy semantics.
Keywords: malloc wxWidgets OpenGL polymorphism doxygen Deep Exploration C ++ object model Reading Notes (2 ).
The following class word statements are known:
// The following statement shows bitwise copy Semantics
Class 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 ".
However, if the class word statement is as follows:
// The following statement does not show bitwise copy Semantics
Class word {
Public:
Word (const string &);
~ Word ();
//...
PRIVATE:
Int CNT;
String & STR;
};
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
Inline word: Word (const word & WD)
{
Str. String: string (WD. Str );
CNT = WD. CNT;
}
A class does not show four situations of "bitwise copy semantics": (1) when the class contains a member object while the latter class declaration has a copy constructor (whether explicitly declared or synthesized)
(2) When the class inherits from a base class and the latter has a copy constructor
In the first two cases, the compiler must insert the "copy constructor call operation" of member or base class into the merged copy constructor.
(3) When the class declares one or more virtual functions
Since the compiler needs to set an initial value for the vptr of each new class object, when the compiler imports a vptr to the class, the class will no longer display bitwise semantics.
Keywords: malloc wxWidgets OpenGL polymorphism doxygen Deep Exploration C ++ object model Reading Notes (2 ).
When a base class object is initialized with its derived class Object content, its vptr copy operation must be secure. If bitwise copy is still used, the vptr of the base class object is set to point to the virtual table of the derived class, which leads to a disaster.
(4) When the class is derived from an inherited string chain with one or more virtual base classes, when a class object uses an object of Its Derived classes as the initial value, to complete the correct initial values of virtual base class pointer/offset, the compiler must synthesize a copy constructor and install some codes to set the initial values of virtual base class pointer/offset, perform necessary memberwise initialization operations on each member and other memory-related operations. In this case, simple bitwise copy is far from enough.
* ** Optimized ***
(1) define a constructor for computing at the user level:
X Bar (const T & Y, const T & Z)
{
X xx;
//... Use Y and Z to process xx
Return xx;
}
The preceding constructor requires that xx be copied to the result produced by the compiler by "memberwise. Therefore, the following constructor can be defined and the value of XX can be calculated directly:
X bar(const T &y,const T &z)
{
return X(y,z);
}
(2) Compare the following initialization operations at the compiler level:
X xx0(1024);
X xx1 = X(1024);
X xx2 = (X)1024;
Specifically, xx0 is the initial value set by a single constructor operation:
Xx0.x: X (1024 );
Xx1 and xx2 call two constructor to generate a temporary object and set it to an initial value of 1024. Then, the temporary object is copied and constructed as the initial value of the explicit object, finally, destructor of Class X is called for this temporary object:
Keywords: malloc wxWidgets OpenGL polymorphism doxygen Deep Exploration C ++ object model Reading Notes (2 ).
X _temp0;
_temp0.X::X(1024);
xx1.X::X(_temp0);
_temp0.X::~X();
From this we can see that the optimization made by the compiler can lead to a significant increase in efficiency when generating machine code. The disadvantage is that you cannot safely plan the side effects of your copy constructor, which must be determined by its execution.
So, do you want to copy the constructor? The copy constructor application forces the compiler to partially optimize your program code. In particular, when a function returns a class object by passing a value, and the class has a copy constructor, this will lead to profound program conversion.
For example, whether memcpy () or memset () is used, it can only run when "classes does not include any internal members generated by the compiler. The following class declares a virtual function, and the compiler generates a vptr for it. If the above function is used, the initial vptr value will be rewritten.
Class shape ...{
Public:
// This will change the internal vptr
SHAPE () {memset (this, 0, sizeof (SHAPE ));};
Virtual ~ SHAPE ();
//...
};
// Expanded Constructor
Shape: shape ()
{
// Vptr must be set before the user's code is executed
_ Vptr_shape = _ vtbl_shape;
// Memset clears the vptr to 0
Memset (this, 0, sizeof (SHAPE ));
}
* ** Initialize the team of Members ***
In the following cases, you must use Member initialization list to allow your program to be compiled smoothly:
(1) When initializing a reference member;
(2) When initializing a const member;
(3) When a constructor of the base class is called and it has a set of parameters;
(4) When a constructor of a member class is called and it has a set of parameters.
Keywords: malloc wxWidgets OpenGL polymorphism doxygen Deep Exploration C ++ object model Reading Notes (2 ).
The member initialization list can sometimes bring about a huge performance improvement. Let's take a look at the following comparison:
class Word {
String _name;
int _cnt;
public:
Word() {
_name = 0;
_cnt = 0;
}
};
// Possible internal expansion result of its constructor
Word: Word ()
{
// Call the default constructor of string
_ Name. String: string ();
// Generate a temporary object
String temp = string (0 );
// Copy _ name from "memberwise"
_ Name. String: Operator = (temp );
// Destroy a temporary object
Temp. String ::~ String ();
_cnt = 0;
}
If the member initialization list is used:
// Better method
Word: word: _ name (0)
{
_ CNT = 0;
}
Word: Word ()
{
// Call String (INT) constructor
_ Name. String: string (0 );
_ CNT = 0;
}
Note that the items in the member initialization list are inserted one by one before the explicit it user code according to the member declaration order in the class. (Therefore, be especially careful when both sides of the expression are member)
The following is an error example:
Class X {
Int I;
Int J;
Public:
// I is assigned to J first, but J does not have an initial value.
X (INT Val): J (VAL), I (j)
{}
...
};
Series of articles:
Deep Exploration C ++ object model Reading Notes (1)
Deep Exploration C ++ object model Reading Notes (3)
Deep Exploration C ++ object model Reading Notes (4)
Deep Exploration C ++ object model Reading Notes (5)
Deep Exploration C ++ object model Reading Notes (6)
Deep Exploration C ++ object model Reading Notes (7)
Last reading note of Deep Exploration C ++ Object Model