on the unreasonable design of C + + construction function
Author: Zhang
In C + +, a constructor is a special function that is invoked at the time of the Component object to initialize the object so that the object can be in a reasonable state before it is used. However, the design of the constructor is not perfect, even some unreasonable characteristics. For example, you qualify a condition with the same name as a class with a constructor name. These features are noteworthy when constructing a C + + compiler. Also, these features should be avoided in future C + + standard revisions or other object-oriented design languages. A number of solutions are also proposed here.
In C + +, any class has one (at least one) constructor, even when no constructor is declared. These constructors are invoked when the object is declared, or when it is dynamically generated. Constructors do a lot of work that is not visible, even if there is no code in the constructor, which includes the allocation of memory to the object and the initialization of the member by way of assignment. The name of the constructor must be the same as the name of the class, but there can be many different overloaded versions to provide, differentiating the version of the constructor by the parameter type. Constructors can be invoked explicitly through user code, or if the code does not exist, it is implicitly inserted by compiling the program. Of course, it is a recommended method to explicitly call through code, because the effect of an implicit invocation may not be what we expect, especially when dealing with dynamic memory allocations. The code invokes a unique constructor by parameter. The constructor does not return a value, although the statement can be returned in the body of the function. Each constructor can instantiate an object in a different way because each class has a constructor, or at least a default constructor, so each object uses the constructor appropriately before it is used.
Because a constructor is a function, his visibility is nothing more than three kinds of public, private, protected. Typically, constructors are declared to be public. If the constructor is declared private or protected, the instantiation of the object is restricted. This is useful in preventing classes from being instantiated by others. The constructor can have any C + + statements, such as a print statement that can be added to the constructor to indicate the location of the call.
Types of constructors
There are many types of constructors in C + +, most commonly used default constructors and copy constructors, and there are some less commonly used constructors. Here are four different constructors.
1, default constructor
The default constructor is a function that has no parameters. In addition, the default constructor can be declared as a parameter default in the argument list. The function of the default constructor is to initialize the object to the default state. If you do not explicitly define a constructor in a class, the compiler automatically creates an implicit constructor that is similar to an empty constructor. He does nothing but produce an instance of an object. In many cases, the default constructor is invoked automatically, such as when an object is declared, causing a call to the default constructor.
2. Copy constructor function
A copy constructor, often called X (x&), is a special constructor that is called by the compiler to perform artifacts and initializations based on other objects of the same class. Its unique parameter (a reference to an object) is immutable (because it is a const type). This function is often used to pass and return the value of a user-defined type during a function call. Copy constructors call the copy constructors and member functions of the base class. If it can, it will be called in a constant way, or it can be called in a very different way.
In C + +, the following three types of objects need to be copied. Therefore, the copy constructor will be invoked.
1 An object passed through the function body in the form of a value
2) An object is returned from the function in a value-passing way
3 An object needs to be initialized with another object
The above situation requires a copy of the constructor call. If the copy constructor is not used in the first two cases, it causes a pointer to point to the memory space that has been deleted. For the third case, the different meanings of initialization and assignment are the cause of the constructor call. In fact, copy constructors are implemented by common constructors and assignment operations. There are a number of reference materials that describe the similarities and differences between copy constructors and assignment operators.
The copy constructor cannot change the object it refers to, for the following reasons: When an object transmits a function in the form of a value, the copy constructor is automatically invoked to generate the object in the function. If an object is passed into its own copy constructor, its copy constructor will be called to copy the object so that replication can pass into its own copy constructor, which can result in an infinite loop.
In addition to being implicitly invoked when an object is passed in a function, the copy constructor is also invoked when the object is returned by the function. In other words, what you get back from the function is just a copy of the object. But again, the copy constructor is called correctly, and you don't have to worry about it.
If there is no explicit declaration of a copy constructor in the class, the compiler will secretly create a function for you to perform a bit copy between the objects (bitwise copy). This implied copy constructor simply associates all of the class members. Many authors will mention this default copy constructor. Note that the difference between this implicit copy constructor and the explicitly declared copy constructor is the way that the member is associated. An explicitly declared copy constructor associates only the default constructor of a class member that is instantiated unless another constructor is invoked when the class is initialized or when the list is constructed.
A copy constructor is a more efficient program because it changes the parameter list of a constructor without having to construct an object again. Designing a copy constructor is a good style, even if the compiled system provides help for you to request a memory default copy constructor. In fact, the default copy constructor can handle many situations.
3. User-defined constructors
User-defined constructors allow objects to be initialized at the same time as they are defined. This constructor can have any type of argument. A user-defined and other type of constructor is 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. Forces the constructor to set a type conversion (implicit or explicit) from the parameter type. In other words, the compiler can invoke the constructor with an instance of any argument. The purpose of this is to create a temporary instance to replace an instance of a parameter type. Note that the standard newly added to C + + keyword Explicit is used to suppress implicit type conversions. However, this feature has not been supported by all compilers. The following is an example of an enforced constructor:
Class A
{
Public:
A (int) {}
};
void f (A) {}
void G ()
{
A my_object= 17;
A a2 = A (57);
A A3 (64);
My_object = 67;
F (77);
}
Like a my_object= 17; This declaration means that a (int) constructor is called to generate an object from an integer variable. Such a constructor is a mandatory constructor.
General characteristics
Here are some of the irrational designs of C + + constructors, and there may be other unreasonable things, of course. But, in most cases, we still have to deal with these features, and we need to explain each one.
1, the constructor can be inline, but do not do so
Generally speaking, most member functions can be preceded by the "inline" keyword and become an inline function, the constructor is no exception, but do not do so. A constructor that is defined as inline is as follows:
Class X
{..........
public:x (int);
:
:
};
inline x::x (int)
{...}
In the above code, the function is not as a separate entity but is inserted into the program code. This refers to efficiency for functions that have only one or two statements, because there is no overhead for calling functions.
The danger of using an inline constructor can be manifested in the definition of 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 cells, the function will produce multiple copies. This allows all copy of the function to be invoked at the start of the program, rather than a copy that the program should call. The fundamental reason for this is that the static function is a real object in the guise of a function.
One thing to keep in mind is that inline is a recommendation rather than a force, and the compiler generates inline code. This means that inline is different from the implementation-related compiler, which can cause many differences. inline functions, on the other hand, may include something more than code. The constructor is declared inline, and all constructors that contain the object and the constructor of the base class need to be invoked. These calls are implied in the constructor. This may create large inline function segments, so it is not recommended to use inline constructors.
2, the constructor does not have any return type
Specifying a return type for a constructor is an error because it introduces the address of the constructor. This means that an error cannot be handled. Thus, whether a constructor succeeds in creating an object will not be determined by the return. In fact, although C + + constructors cannot be returned, there is a way to determine if the memory allocation is successful. This approach is a mechanism built inside the language to deal with emergencies. A predefined function pointer New-handler, which can be set to a user-defined function that fails to deal with the new operator, which can perform any action, including setting the error flag, re applying for memory, exiting the program, or throwing an exception. You can safely use the New-handler built in the system. The best way to make a constructor emit 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 with exception handling, it might be a better idea to initialize in another function. In this way, the programmer can secure the Component object and get a reasonable pointer. The initialization function is then invoked. If initialization fails, the object is purged directly.
3. Constructors can not be declared as static
In C + +, objects of each class have a copy of the class data member. However, static members do not, but all objects share a static member. Static Functions Act on the actions of a class, not on an object. You can call a static function with the class name and action control operator. One of these exceptions is the constructor, because it violates the object-oriented concept.
A similar phenomenon about these is static objects, where the initialization of static objects occurs at the beginning of a 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, the static variable is initialized at the beginning. Usually these objects are composed of two parts. The first part is the data segment, and the static variable is read into the global data segment. The second section is a static initialization function that is invoked before the main () function. We found that some compilers did not check for initialization reliability. So what you get is an uninitialized object. The solution is to write a wrapper function that places all the references of the static objects in the call to the function, and the example above should be rewritten in this way.
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. Constructors cannot be virtual functions
A fictional function means that a programmer can create an object without knowing the exact type of the object before it is run. It is impossible to make a fictional function in C + +. The most common place to encounter this is when I/O is implemented on an object. Even if an adequate class's internal information is given in a file, a method must be found to instantiate the corresponding class. However, experienced C + + programmers have other ways to simulate fictional functions.
Simulating a virtual function requires that the calling constructor be specified when the object is created, and the standard method is to invoke the virtual member function. Unfortunately, C + + does not support fictional functions in syntax. To circumvent this limitation, some out-of-the-box methods can determine the object of the widget at run time. These are equivalent to fictional functions, but this is something that does not exist in C + +.
The first method is to manually implement the selection using a switch or If-else selection statement. In the following example, the selection is based on the type_info construct of the standard library, which is supported by open Run-time type information. But 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 to new Child1;
else if (strcmp (Type_name,typeid
(Child2). Raw_name ()) = = 0)
return to new Child2;
Else
{
Throw exception
("Unrecognized type name passed");
return 0X00; Represent NULL
}
}
This implementation is very straightforward and requires programmers 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 subclasses.
A more object-oriented method class solves a fictional function called a specimen instance. Its basic idea is to generate some global examples in the program. These instances only exist in the mechanism of creating a function:
Class Base
{
Public
staticbase* Make_object (const char* TypeName)
{
if (!exemplars.empty ())
{
base* end = * (Exemplars.end ());
List<base*>::iterator iter =
Exemplars.begin ();
while (*iter!= end)
{
base* e = *iter++;
if (strcmp (TypeName,
E->get_typename ()) = = 0)
return E->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 list<base*> exemplars;
};
List<base*> Base::exemplars;
T must be a concrete class
Derived from Base, above
Template<class t>
Class Exemplar:public T
{
Public
Exemplar ()
{
Exemplars.push_back (this);
}
~exemplar ()
{
Exemplars.remove (this);
}
};
Class Child:public Base
{
Public
~child ()
{
}
base* Clone () const
{
return to new child;
}
};
Exemplar<child> Child_exemplar;
In this design, the programmer to create a class to do is to create a corresponding exampler<t> class. Note that in this example, the specimen is an instance of its own specimen class. This provides a method of university instantiation.
5. Create a default constructor
When inheritance is used, the province constructor is invoked. More specifically, when the last class of the inheritance hierarchy is constructed, the constructors of all base classes are called before the derived base class, for example, looking at the following code:
#include <iostream.h>
Class Base
{
int x;
Public:
Base (): X (0) {}//the NULL constructor
Base (int a): X (a) {}
};
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 did not provide any initialization functions in Gamma's header file. The compiler uses the default constructor for the base class. But because you provide a constructor, the compiler does not provide any default constructors. As you can see the code that contains the default constructor, if you delete the default constructor, the compilation cannot pass.
If you introduce a secondary effect in the constructor of a base class, such as opening a file or applying for memory, the programmer has to make sure that the intermediate base class does not initialize the virtual base class. That is, only the constructor of the virtual base class can be invoked.
The provincial constructor of the virtual base class completes the initialization of a parameter that does not require any dependent on the derived class. You add an init () function and then call it from another function in the virtual base class, or in a constructor in another class (you make sure it only calls once).
6, can not get the address of the constructor
In C + +, a constructor cannot be passed as a function pointer, nor can a pointer to a constructor be passed directly. Allowing these allows you to create an object by calling the pointer. One way to achieve this is by using a static function that creates and returns a new object. A pointer to such a function is used where the new object is needed. Here is an example:
Class A
{
Public
A (); Cannot take the address of this
Constructor directly
static * Createa ();
This function creates a new a object
On the heap and returns a pointer to it.
A pointer to that function can be passed
In lieu's a pointer to the constructor.
};
This method is designed simply by placing the abstract class in the header file. This leaves a problem for new, because the exact type must be visible. The above static function can be used to wrap hidden subclasses.
7. A bit copy is not available in the dynamically requested memory class
In C + +, if a copy constructor is not provided, the compiler automatically generates one. This copy constructor generates a bit copy of an instance of an object. This is fine for classes that do not have pointer members, but not for classes that use dynamic applications. To clarify this point, imagine an object passing through a function in the form of a value, or returning from a function, where the object is copied in the form of a copy. This bit copy has no effect on classes that contain pointers to other objects (see Figure 2). When a class containing pointers passes in a value, the object is copied, including the address of the pointer, and the new object is scoped to this function. At the end of the function, unfortunately, the destructor is destroying this object. As a result, the object's pointer is removed. This causes the pointer of the original object to point to an empty memory area-an error. A similar scenario occurs when the function returns.
This problem can be solved simply by defining a copy constructor that contains a memory request in the class, called a deep copy, that allocates memory to each object in the heap.
8, the compiler can implicitly specify the mandatory constructor
Because the compiler can implicitly select a mandatory constructor, you lose the option to invoke the function. If you need control, do not declare a constructor that has only one parameter, instead, define the helper function to be responsible for the conversion, as in the following example:
#include <stdio.h>
#include <stdlib.h>
Class Money
{
Public
Money ();
Define conversion functions that can
Called explicitly.
Static Money Convert (char * ch)
{return money (CH);}
Static Money Convert (double D)
{return money (d);}
void Print () {printf ("%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 above code, the Force constructor is defined as private and cannot be used for type conversions. However, it can be invoked explicitly. Because the conversion functions are static, they can complete the call without referencing any one object.
Summarize
The point to be clarified here is that we are all familiar with ANSI C + + acceptable. Many compilers have their own syntax revisions to ANSI C + +. These may vary according to the compiler. It is clear that many compilers do not handle these points well. Exploring these points is caused by the attention of compilation constructs, but also in the process of C + + standardization to remove some flaws.
Reference documents:
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 Josee Lajoie. C + + Primer, 3rd ed., Addison-wesley, Reading, MA, 1998.
8. Skelly, C. "Getting A Handle on the New-handler," C + +, 4 (2): 1-18, February 1992.
9. Coggins, J. M. "Handling Failed constructors gracefully," C + +, 4 (1): 20-22, January 1992.
Sabatella, M. "Laser Evaluation of C + + Static constructors," Sigplan notices, (6): 29-36 (June 1992).
Eckel, B. "Virtual constructors," C + +, 4 (4): 13-16,may 1992.
Coplien, James O. Advanced C + +: Programming Styles and Idioms, Addison-wesley, Reading, MA, 1992.
From: http://www.newasp.net/tech/program/20166.html
Author: Zhang
Thanks to the hard work of Zhang Peers.