In C ++, constructor is a special function called when the component object is called. Its purpose is to initialize the object, so that the object can be in a reasonable State before it is used. However, the design of constructor functions is not perfect, and there are even some unreasonable features. For example, the condition that the constructor name and class name are the same is limited. These features are worth noting when constructing the C ++ compiler. Also, these features should be avoided in the future when C ++ standards are revised or other object-oriented design languages are formulated. Some solutions are also proposed here.
In C ++, any class has at least one constructor, even when no constructor is declared. When an object is declared or dynamically generated, these constructors are called. The constructor does a lot of invisible work, even if the constructor does not have anyCodeThese tasks include allocating memory to objects and initializing members by assigning values. The name of the constructor must be the same as the name of the class, but there may be many different overloaded versions to provide. The parameter types are used to distinguish the constructor versions. The constructor can be called explicitly through user code, or when the Code does not existProgram. Of course, explicit calling through code is a recommended method, because the implicit calling effect may not be expected, especially in dealing with dynamic memory allocation. The Code calls a unique constructor through parameters. The constructor does not return values, even though it can return statements in the function body. Each constructor can instantiate an object in a different way. Because each class has a constructor, at least the default constructor, each object uses the constructor before use. Call 1 of the constructor.
Figure 1. The activities involved in the execution of a constructor
Because a constructor is a function, its visibility is nothing more than three types of public, private, and protected. Generally, constructors are declared as public. If the constructor is declared as private or protected, Object Instantiation is restricted. This is effective in preventing classes from being instantiated by others. The constructor can have any c ++ statements. For example, a print statement can be added to the constructor to indicate the position of the call.
Type of Constructor
There are many types of constructor in C ++. The most commonly used default constructor and copy constructor also have some uncommon constructor. The following describes four types of constructors.
1. default constructor
The default constructor is a function without parameters. In addition, the default constructor can also be declared in the parameter list as the default parameter value. The default constructor initializes an object to the default state. If no constructor is explicitly defined in the class, the compiler automatically creates a constructor, which is similar to an empty constructor. He does nothing except the instance that generates the object. In many cases, the default constructor is automatically called. For example, when an object is declared, it will cause the call of the default constructor.
2. copy constructors
Copy constructor, often referred to as X (X &), is a special constructor called by the compiler to complete components and initialization of other objects based on the same class. Its unique parameter (Object Reference) is immutable (because it is of the const type ). This function is often used to pass and return values of user-defined types during function calls. The copy constructor calls the copy constructor and member functions of the base class. If possible, it will be called in constant mode, and can also be called in a very large way.
In C ++, the following three objects need to be copied. Therefore, the copy constructor is called.
1). An object passes in the function body as a value.
2). An object is returned from the function by passing values.
3) an object needs to be initialized through another object
In the above cases, you need to copy the call of the constructor. If you do not use the copy constructor In the first two cases, a pointer is directed to the deleted memory space. In the third case, the difference between initialization and assignment is the reason for calling the constructor. In fact, the copy constructor is implemented by the common constructor and the assignment operation assignment. There are many references describing the similarities and differences between the copy constructor and the value assignment operator.
Copying a constructor cannot change the object referenced by it. The reason is as follows: when an object transmits a function by passing a value, the copy constructor is automatically called to generate objects in the function. If an object is passed in to its own copy constructor, its copy constructor will be called to copy this object so that it can pass in its own copy constructor, this leads to an infinite loop.
In addition to being implicitly called when an object is passed into a function, the copy constructor is also called when the object is returned by the function. In other words, all you get from the function is a copy of the object. But likewise, the copy constructor is called correctly, so you don't have to worry about it.
If no copy constructor is explicitly declared in the class, the compiler will create a function for you to perform bitwise copy between objects ). This implicit copy constructor simply associates all class members. Many authors will mention this default copy constructor. It is noted that the implicit copy constructor differs from the explicit copy constructor In the joining mode for members. The explicitly declared copy constructor is associated with the default constructor of the instantiated class members unless another constructor is called during class initialization or the constructor list.
Copying constructor is more efficient because it does not need to change the parameter list of constructor when constructing an object. Designing a copy constructor is a good style. Even if it is provided by the compilation system, you can apply for a default copy constructor in the memory. In fact, the default copy constructor can handle many situations.
3. User-Defined Constructor
User-Defined constructors allow objects to be initialized at the same time when they are defined. This constructor can have any type of parameters. A user-defined constructor and other types are embodied in the class mystring:
Class mystring
{......
Public: mystring (); // default constructor
Mystring (mystring & SRC)
// Copy constructor
Mystring (char * SCR );
// Coercion Constructor
Mystring (char SCR [], size_t Len );
// User-Defined Constructor
};
4. Force Constructor
In C ++, you can declare a constructor with only one parameter for type conversion. Force the constructor to define a type conversion (implicit or explicit) from the parameter type ). In other words, the compiler can use any parameter instance to call the constructor. The purpose is to create a temporary instance to replace a parameter-type instance. Note that the standard new C ++ keyword explicit is used to prohibit implicit type conversion. However, this feature has not been supported by all compilers. The following is an example of a forced constructor:
Class
{
Public:
A (INT ){}
};
Void F (){}
Void g ()
{
A my_object = 17;
A a2 = a (57 );
A A3 (64 );
My_object = 67;
F (77 );
}
Such declaration means that the (INT) constructor is called to generate an object from an integer variable. Such a constructor is a forced constructor.
General Features
Below are some unreasonable designs of C ++ constructor. Of course, there may be other unreasonable ones. However, in most cases, we still need to deal with these features, which should be explained one by one.
1. constructor can be inline, but do not.
Generally, most member functions can add the "inline" keyword in front to become inline functions. constructors are no exception, but do not do this! A constructor defined as inline is as follows:
Class X
{..........
Public: X (INT );
:
:
};
Inline X: X (INT)
{...}
In the code above, the function is not inserted into the program code as a separate entity. Efficiency is mentioned for functions with only one or two statements, because there is no overhead for calling a function.
The danger of using an inline constructor can be reflected in defining a static inline constructor. In this case, the static constructor should be called only once. However, if the header file contains a static inline constructor and is included by other units, the function will generate multiple copies. In this way, when the program starts, all function copies will be called, instead of a copy that the program should call. The root cause is that static functions are real objects disguised as functions.
One thing to remember is that inline is recommended rather than forced, and the compiler generates Inline code. This means that there may be many differences between inline and implementation-related compilers. On the other hand, inline functions may include more things than code. Constructor is declared as inline. All constructor containing objects and constructors of the base class must be called. These calls are implicitly included in the constructor. This may create a large inline function segment. Therefore, inline constructors are not recommended.
2. the constructor does not have any return types.
Specifying a return type for a constructor is an error because the constructor address is introduced. This means that the error cannot be handled. In this way, whether a constructor successfully creates an object cannot be determined by returning it. In fact, although the C ++ constructor cannot return, there is also a way to determine whether the memory allocation is successful. This method is built into the language to handle emergencies. A predefined function pointer, new-handler, can be set as a user-defined function for dealing with new operator failures. This function can perform any action, including setting the error mark, applying for memory again, exiting the program, or throwing an exception. You can use the built-in New-handler with peace of mind. The best way to make the constructor send an error signal is to throw an exception. Throwing an exception in the constructor will clear any objects created before the error and the allocated memory.
If the constructor fails to use Exception Processing, initialization in another function may be a better idea. In this way, programmers can safely construct component objects and obtain a reasonable pointer. Then, the initialization function is called. If initialization fails, the object will be cleared directly.
3. the constructor cannot be declared as static.
In C ++, each class object has a copy of the class data member. However, static members do not share a static member with all objects. Static Functions Act on class operations rather than objects. You can call a static function by using the class name and the control operator. One of these exceptions is the constructor, because it violates the object-oriented concept.
A similar phenomenon about these is static objects. static object initialization is performed at the beginning of the Program (before the main () function ). The following code explains this situation.
Myclass static_object (88, 91 );
Void bar ()
{
If (static_object.count ()> 14 ){
...
}
}
In this example, static variables are initialized at the beginning. These objects are usually composed of two parts. The first part is the data segment. Static variables are read to the global data segment. The second part is the static initialization function, which is called before the main () function. We found that some compilers did not check the initialization reliability. So what you get is an uninitialized object. The solution is to write an encapsulated function and place all references to static objects in the function call. The above example should be rewritten as follows.
Static myclass * static_object = 0;
Myclass *
Getstaticobject ()
{
If (! Static_object)
Static_object =
New myclass (87, 92 );
Return static_object;
}
Void bar ()
{
If (getstaticobject ()-> count ()> 15)
{
...
}
}
4. the constructor cannot be a virtual function.
A virtual constructor means that a programmer can create an object without knowing the exact type of the object before running it. Virtual constructors cannot be implemented in C ++. This is usually the case when I/O is implemented on the object. Even if the internal information of enough classes is provided in the file, you must find a way to instantiate the corresponding class. However, experienced C ++ programmers may have other ways to simulate virtual constructors.
To simulate a virtual function, you must specify the called constructor when creating an object. The standard method is to call a virtual member function. Unfortunately, C ++ does not support virtual constructor syntax. To bypass this restriction, some existing methods can determine the object of the component at runtime. These are equivalent to virtual constructors, but they do not exist in C ++.
The first method is to use the switch or if-else selection statement to manually implement the selection. In the following example, the type is constructed based on the type_info of the standard library and supported by enabling runtime type information. However, you can also use virtual functions to implement rtti.
Class base
{
Public:
Virtual const char * get_type_id () const;
Staticbase * make_object
(Const char * type_name );
};
Const char * base: get_type_id () const
{
Return typeid (* This). raw_name ();
}
Class Child1: public Base
{
};
Class child2: public Base
{
};
Base * base: make_object (const char * type_name)
{
If (strcmp (type_name,
Typeid (Child1). raw_name () = 0)
Return new Child1;
Else if (strcmp (type_name, typeid
(Child2). raw_name () = 0)
Return new child2;
Else
{
Throw exception
("Unrecognized type name passed ");
Return 0x00; // represent null
}
}
This implementation is very direct. It requires the programmer to save a table of all classes in main_object. This destroys the encapsulation of the base class, because the base class must know its own subclass.
A more object-oriented method class solves the virtual constructor called a specimen instance. Its basic idea is to generate some global instances in the program. These instances only exist in the virtual constructor mechanism:
Class base
{
Public:
Staticbase * make_object (const char * typename)
{
If (! Exemplars. Empty ())
{
Base * end = * (exemplars. End ());
List: Iterator iter =
Exemplars. Begin ();
While (* iter! = End)
{
Base * E = * ITER ++;
If (strcmp (typename,
E-> get_typename () = 0)
Returne-> clone ();
}
}
Return 0x00 // represent NULL;
}
Virtual ~ Base (){};
Virtual const char * get_typename () const
{
Return typeid (* This). raw_name ();
}
Virtual base * clone () const = 0;
Protected:
Static listExemplars;
};
ListBase: exemplars;
// T must be a concrete class
// Derived from base, abve
Template
Class exemplar: Public t
{
Public:
Exemplar ()
{
Exemplars. push_back (this );
}
~ Exemplar ()
{
Exemplars. Remove (this );
}
};
Class child: public Base
{
Public:
~ Child ()
{
}
Base * clone () const
{
Return new child;
}
};
Exemplar Child_exemplar;
In this design, the programmer must create a corresponding exampler when creating a class. Class. Note that in this example, the specimen is an instance of its own specimen class. This provides a way to instantiate a university.
5. Create a default constructor
When the inheritance is used, the constructor is called. More specifically, when the classes at the last layer of the hierarchy are constructed, the constructors of all base classes are called before the base classes are derived. For example, see the following code:
# Include
Class base
{
Int X;
Public:
Base (): x (0) {}// the null Constructor
Base (int A): X (){}
};
Class Alpha: virtual public Base
{
Int y;
Public:
Alpha (int A): Base (A), y (2 ){}
};
Class BETA: virtual public Base
{
Int Z;
Public:
Beta (int A): Base (A), Z (3 ){}
};
Class GAMMA: Public Alpha, Public Beta
{
Int W;
Public:
Gamma (int A, int B): alpha (A), beta (B), w (4 ){}
};
Main ()
{.....
}
In this example, we have not provided any initialization functions in the Gamma header file. The compiler uses the default constructor for the base class. However, because you provide a constructor, the compiler does not provide any default constructor. As you can see in the code that contains the default constructor, if you delete the default constructor, compilation will fail.
If the constructor of the base class introduces some sub-effects, such as opening a file or applying for memory, the programmer must ensure that the intermediate base class does not initialize the virtual base class. That is, only the constructors of the virtual base class can be called.
The constructor of the virtual base class does not need to initialize any parameters dependent on the derived class. You add an Init () function and then call it from other functions of the virtual base class, or in the constructor of another class (you make sure it is called only once ).
6. the constructor address cannot be obtained.
In C ++, constructor cannot be passed as a function pointer, or pointer to constructor cannot be passed directly. You can call a pointer to create objects. One way to achieve this is to use a static function that creates and returns a new object. The pointer to such a function is used for the new object. The following is an example:
Class
{
Public:
A (); // cannot take the address of this
// Constructor directly
Static A * createa ();
// This function creates a new a object
// On the heap and returns a pointer to it.
// A pointer to this function can be passed
// In lieu of a pointer to the constructor.
};
This method is easy to design. You only need to put the abstract class into the header file. This leaves a problem for new because the exact type must be visible. The above static functions can be used to wrap hidden subclasses.
7. Bit copy is not feasible in the dynamically applied memory class
In C ++, if a copy constructor is not provided, the compiler automatically generates one. The generated copy constructor performs a bit copy on the object instance. This is nothing for a class without pointer members. However, this is not the case for a dynamically applied class. To clarify this, imagine that an object passes in a function as a value or returns it from the function. The object is copied as a copy. This bitcopy has no effect on classes containing pointers to other objects (see figure 2 ). When a class containing pointers passes in a function as a value, the object is copied, including the pointer address, and the scope of the new object is this function. Unfortunately, at the end of the function, the Destructor will destroy this object. Therefore, the object pointer is deleted. This causes the pointer of the original object to point to an empty memory area-an error. Similar situations occur when the function returns.
Figure 2. The automatic copy constructor that makes a bitwise copy of the class.
This problem can be solved simply by defining a copy constructor containing memory requests in the class. This kind of problem is called Deep copy, which allocates memory to each object in the heap.
8. the compiler can implicitly specify a forced constructor.
Because the compiler can implicitly select a forced constructor, you lose the option to call the function. If you need control, do not declare a constructor with only one parameter. Instead, define the Helper function to take charge of the conversion, as shown in the following example:
# Include
# Include
Class money
{
Public:
Money ();
// Define conversion functions that can only be
// Called explicitly.
Static money convert (char * Ch)
{Return money (CH );}
Static money convert (double D)
{Return money (d );}
Void print () {printf ("\ n % F", _ amount );}
PRIVATE:
Money (char * Ch) {_ amount = atof (CH );}
Money (double D) {_ amount = D ;}
Double _ amount;
};
Void main ()
{
// Perform a conversion from type char *
// To type money.
Money Account = money: Convert ("57.29 ");
Account. Print ();
// Perform a conversion from type double to type
// Money.
Account = money: Convert (33.29 );
Account. Print ();
}
In the code above, the Force constructor is defined as private and cannot be used for type conversion. However, it can be explicitly called. Because the conversion functions are static, they can be called without referencing any object.
Summary
One thing to be clarified is that all mentioned here are accepted by the well-known ansi c ++. Many compilers have made their own syntax revisions to ansi c ++. These may vary depending on the compiler. Obviously, many compilers cannot handle these issues well. The reason for this exploration is the attention of the compilation structure, and some flaws are also removed during the C ++ standardization process.
References:
1. stroustrup, Bjarne. the C ++ programming language, 3rd ed ., addison-Wesley, reading, Ma, 1997. 2. ellis, Margaret and Bjarne stroustrup. the annotated C ++ reference manual, Addison-Wesley, reading, Ma, 1990. 3. stroustrup, Bjarne. the design and evolution of c ++, Addison-Wesley, reading, Ma, 1994. 4. murry, Robert B. c ++ strategies and tactics, Addison-Wesley, reading, Ma, 1993. 5. farres-Casals, J. "proving correctness of constructor implementations," mathematical foundations of computer science 1989 proceedings. 6. breymann, Ulrich. designing components with the c ++ STL, Addison-Wesley, reading, Ma, 1998. 7. lippman, Stanley and Joseph Lajoie. c ++ primer, 3rd ed ., addison-Wesley, reading, Ma, 1998. 8. skelly, C. "getting a handle on the new-handler," C ++ report, 4 (2): 1-18, February 1992. 9. coggins, J. m. "handling failed constructors gracefully," C ++ report, 4 (1): 20-22, January 1992. 10. sabatella, M. "laser evaluation of c ++ static constructors," sigplan notices, 27 (6): 29-36 (June 1992 ). 11. eckel, B. "Virtual constructors," C ++ report, 4 (4): 13-16, May 1992. 12. coplien, James O. advanced C ++: programming styles and idioms, Addison-Wesley, reading, Ma, 1992.