C ++ Object Model -- Construction of Default Constructor (chapter 2)
Chapter 2 The most common complaint about C ++ In The constructors of Constructor is that The compiler has done too many things on The back of programmers. the Conversion operator is the most commonly referenced example.
2.1 Default Constructor construction operation C ++ Annotated Reference Manual(ARM) indicates that default constructors... is generated by the compiler when necessary.When the keyword eye is needed, who needs it? What do you do? Take a look at the following code:
class Foo {
public:
int val;
Foo *pnext;
};
void foo_bar() {
Foo bar;
if (bar.val || bar.pnext)
// ... do something
}
In this example, the correct program requires Foo to have a default constructor. You can initialize the two members of Foo to 0. The above Code does not meet the requirements described by ARM. the difference lies in the need of a program and the need of a compiler. the above code will not synthesize a default constructor.
So
When will a default constructor be merged? When the compiler needs it! In addition, the constructor synthesized only executes the actions required by the compiler.That is to say, even if a default constructor needs to be synthesized for class Foo, the constructor will not initialize two data members val and pnext to 0. in order for the previous code to be correctly executed, the designer of class Foo must provide an explicit default constructor and initialize the two members as appropriate.
C ++ Standard has modified the ARM statement, although its behavior is actually the same. C ++ Standard indicates that for class X, if no user-declared constructor exists, a default constructor is implicitly declared .... A default constructor that is implicitly declared will be a trivial constructor...
Member Class Object with Default constructor if a class does not have any constructor, but it contains a member object, and the latter has default constructor, the implicit default constructor of this class is nontrivial, the compiler needs to synthesize a default constructor for this class. However, this merging operation only happens when the constructor needs to be called.
So there is an interesting question:
In different C ++ compilation modules, how does the compiler avoid merging multiple default constructor(For example, one is A. C synthesis, and the other is B .C synthesis )? 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.
For example, in the following program snippet, the compiler synthesize a default constructor for the class Bar:
// Camera.cpp: defines the entry point for the console application.
//
#include "stdafx.h"
#include <opencv2 \ opencv.hpp>
using namespace cv;
using namespace std;
static CvMemStorage * storage = NULL;
static CvHaarClassifierCascade * cascade = NULL;
const char * cascadePath = "D: \\ opencv \\ sources \\ data \\ haarcascades \\ haarcascade_frontalface_alt_tree.xml";
int _tmain (int argc, _TCHAR * argv [])
{
/ *********** Initialize a camera catcher *********** /
CvCapture * capture = cvCreateCameraCapture (0);
cvNamedWindow ("Camera");
/ *********** Initialize variables related to face detection *********** /
IplImage * cameraImage = NULL;
storage = cvCreateMemStorage (0);
cascade = (CvHaarClassifierCascade *) cvLoad (cascadePath);
while ((cameraImage = cvQueryFrame (capture))! = NULL)
{
// cvShowImage ("Camera", cameraImage);
cvWaitKey (1);
/**********Grayscale***********/
IplImage * grayImage = cvCreateImage (cvSize (cameraImage-> width, cameraImage-> height), 8,1);
cvCvtColor (cameraImage, grayImage, CV_BGR2GRAY);
/**********Face Detection***********/
cvClearMemStorage (storage);
CvSeq * objects = cvHaarDetectObjects (grayImage, cascade, storage, 1.1, 2, 0, cvSize (30, 30));
/ ********** Draw test results *********** /
for (int i = 0; i <(objects? objects-> total: 0); i ++)
{
CvRect * rect = (CvRect *) cvGetSeqElem (objects, i);
cvRectangle (cameraImage, cvPoint (rect-> x, rect-> y), cvPoint (rect-> x + rect-> width, rect-> y + rect-> height), cvScalar (0.0,255));
}
cvShowImage ("Camera", cameraImage);
}
return 0;
}
The merged Bar default constructor contains the necessary code. It can call the default constructor of class Foo to process the member object Bar: foo, but it does not generate any code to initialize Bar: str. initializing Bar: foo is the responsibility of the compiler. Initializing Bar: str is the responsibility of the programmer. The merged default constructor may be as follows:
// Bar's default constructor may be synthesized like this
// The default constructor of class Foo is called by member foo
inline Bar :: Bar () {
// C ++ pseudocode
foo.Foo::Foo ();
}
Note that the synthesized default constructor only meets the needs of the compiler, rather than the needs of the program. To make the program fragment correctly executed, the character pointer str also needs to be initialized. assume that the programmer provides str initialization through the following default constructor:
// The default constructor defined by the programmer
Bar :: Bar () {str = 0;}
Now the program requirements are met, but the compiler still needs to initialize member object foo. Since the default constructor has been explicitly defined, the compiler cannot synthesize the second one.
The compiler takes action: if class A contains one or more member class objects, each constructor of class A must call the default constructor of every member classes, and the compiler will expand the existing constructors, install some codes to call the required default constructors before the user code is executed. in the previous example, the expanded constructors may look like this:
// expanded default constructor
// C ++ pseudocode
Bar :: Bar () {
foo.Foo::Foo (); // append the supplier code
str = 0; // explicit user code
}
If multiple class member objects require constructor InitializationWhat will happen?
The C ++ language requires that each constructors be called in the declared order of member objects in class.. This is done by the compiler. It inserts program code for each constructor and calls the default constructor associated with each member in the order declared by member. these codes are installed before the explicit user code. if there are three classes as follows:
class Dopey {
public:
Dopey ();
};
class Sneezy {
public:
Sneezy (int);
Sneezy ();
};
class Bashful {
public:
Bashful ();
};
And a class Snow_White:
class Snow_White {
public:
Dopey dopey;
Sneezy sneezy;
Bashful bashful;
private:
int number;
};
If Snow_White does not define default constructor, a nontrivial constructor will be merged and the default constructor of Dopey, Sneezy, and Bashful will be called in order. However, if Snow_White defines the following default constructor:
// The default constructor written by the programmer
Snow_White :: Snow_White (): Sneezy (1024) {
member = 2048;
}
It will be expanded to:
// default constructor expanded by the compiler
// C ++ pseudocode
Snow_White :: Snow_White (): Sneezy (1024) {
// insert member class object
// call its constructor
dopey.Dopey :: Dopey ();
sneezy.Sneezy :: Sneezy (1024);
bashful.Bashful :: Bashful ();
// Explicit user mode
member = 2048;
}
The sequence is the member objects of the class, the class constructor.
Base Class with Default Constructor if a class without any constructors derives from a base class with default constructor, the default constructor of this derived class will be considered nontrivial, therefore, it needs to be merged. It will call the default constructor of the base classes on the previous layer (according to their declaration order). For a subsequent derived class, the constructor in this synthesis is no different from the default constructor explicitly provided.
What if the designer provides multiple constructor, but none of them have default constructor? The compiler will expand every existing constructor and add the program code used to call all the necessary default constructors. It will not synthesize a new default constructor, this is because other constructor provided by the user exists. If member class objects with default constructors exist at the same time, the default constructor will also be called after all base class constructors are called. As you can see,
The constructor starts from the root of the class hierarchy. At each layer, the base class constructor is called first, and then the constructor of the member object is called..
The default constructor must be synthesized for a Class with a Virtual Function:
1. class declares (or inherits) a virtual function
2. The class is derived from an inherited string, with one or more virtual base classes.
In either case, due to the lack of user-declared constructors, the compiler will detail the required information for merging a default constructor. The following program snippet is used as an example:
class Widget {
public:
virtual void flip () = 0;
// ...
};
void flip (const Widget & widget) {
widget.flip ();
}
// Assume that Bell and Whistle are both derived from Widget
void foo () {
Bell b;
Whistle w;
flip (b);
flip (w);
}
The following two extensions will occur during compilation:
1. A virtual function table (vtbl in cfront) 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, the virtual invocation of the widget. flip () is rewritten to use the vptr of the widget and the flip () entry in the vtbl:
// Transform of virtual invocation of widget.flip ()
(* widget.vptr [1]) (& widget)
Where:
1 indicates the fixed index of flip () in virtual table
& Widget indicates the this pointer of a flip () function entity to be handed over.
To make this mechanism effective,
The compiler must set an initial value for the vptr of each Widget (or its derived class) object and place the appropriate virtual table address. For every constructor defined by the class, the compiler inserts some code to do this.(See section 5.2 ). For classes that do not declare any constructor, the compiler will synthesize a default constructor for them to correctly initialize the vptr of each class object.
The implementation of ClassVirtual Base Class with a Virtual base class varies greatly between different compilers. However,
The common point of each implementation method is that the virtual base class must be positioned in each of its derived class objects and can be properly written during the execution period.In the following code:
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;
};
// Cannot resolve (resolve) the position of pa-> X :: i during compilation
void foo (const A * pa) {
pa-> i = 1024;
}
main ()
{
foo (new A);
foo (new C);
// ...
}
The compiler cannot determine the actual offset position of X: I accessed by pa in foo (), because the actual type of pa can be changed.
The compiler must change the code that executes the access operation so that X: I can be delayed until the execution period is reached.. In the past, cfront installed a pointer in each virtual base classes of the derived class object. All operations to access a virtual base class through reference or pointer can be completed through related pointers. Foo can be rewritten as follows to comply with this implementation policy:
// Possible compiler conversion operations
void foo (const A * pa) {
pa-> _ vbcX-> i = 1024;
}
_ VbcX indicates the pointer generated by the compiler, pointing to virtual base class X.
_ VbcX (or something made by the compiler) is completed during class object Construction. For each constructor defined by the class, the compiler inserts the code that allows each virtual base class to access operations during the execution period. If the class does not declare any constructor, the compiler must synthesize a default constructor for it.
There are four situations in summary, which will cause the compiler to synthesize a default constructor 'for classes without declaring a constructor. C ++ Stardard calls the compositing implicit nontrivial default constructor. The constructor can only meet the needs of the compiler (rather than the program. It can complete the task by calling the default constructor of the member object or base class, or initializing its virtual function mechanism or virtual base class mechanism for each object. There are no such four cases and no constructor classes are declared. Some of them are implicit trivial default constructor which will not actually be merged.
In the synthesized default constructor, only base class subjects and member class objects will be initialized, and all other nonstatic data member, such as integer, integer pointer, and integer data will not be initialized, these initialization operations may be required for the program, but not for the compiler. if the program needs a default constructor that sets a pointer to 0, it should be provided by programmers.
C ++ beginners generally have two common misunderstandings:
1. Any class will be merged if default constructor is not defined
2. The default constructor synthesized by the compiler will explicitly set the default value of each data member in the class.
Both are incorrect.