[10] Constructor (Part of C FAQ Lite, Copyright © 1991-2001, Marshall Cline, Cline@parashift.com)
体,, nicrosoft @ sunistudio.com (East day production room, East day document)
FAQS in section [10]:
[10.1] What is the constructor do? [10.2] List x; and List x (); Is there a difference? [10.3] How can a constructor can call another constructor directly? [10.4] Fred class's default constructor always fred :: fred ()? [10.5] Which constructor will be called when I establish an array of Fred object? [10.6] Constructor should be "initialized list" or "assignment"? [10.7] Can I use the THIS pointer in the constructor? [10.8] What is "Named Constructor Idiom"? [10.9] Why not initialize static member data in the initialization list of constructors? [10.10] Why get a link error with a class of static data members? [10.11] What is "Static Initialization Order FiaSco"? [10.12] How to prevent "Static Initialization Order FiaSco?" [10.13] How to prevent "Static Initialization Order FiaSco" for static data members? [10.14] How to deal with the failure of constructor? [10.15] What is "Named Parameter Idiom"?
[10.1] What is the constructor do?
The constructor is not established from an unsuccessful.
The constructor is like the "initialization function". It turns a series of casual memory bits into live objects. At least it is to initialize the domain used inside the object. It also assigns resources (memory, files, signals, sockets, etc.)
"CTOR" is a typical abbreviation for constructor.
[TOP | BOTTOM | Previous Section | Next Section]
[10.2] List x; and List x (); Is there a difference?
Have a very big difference!
Suppose List is a name of a class. Then the function f () declares a partial list object, name is x:
Void f () {List x;
// local object named x (of class list)
// ...
}
However, in the function g () declared a function name X (), it returns a list:
Void g () {List x ();
// Function named x (That Returns a List)
// ...
}
[TOP | BOTTOM | Previous Section | Next Section]
[10.3] How can a constructor can call another constructor directly?
Not.
Note: If you call another constructor, the compiler will initialize a temporary local object; rather than initializing the THIS object. You can share the two constructor in combination with a default parameter or in a private member function init ().
[TOP | BOTTOM | Previous Section | Next Section]
[10.4] Fred class's default constructor always fred :: fred ()? Do not. "Default Constructor" is a constructor that can be called without parameter. Therefore, a constructor without parameters is of course the default constructor:
Class fred {public: fred ();
// Default constructor: can be called without parameter
// ...
}
However, if the parameters are provided with default, the default constructor with parameters is also possible:
Class Fred {public: fred (int i = 3, int J = 5);
// Default constructor: can be called without parameter
// ...
}
[TOP | BOTTOM | Previous Section | Next Section]
[10.5] Which constructor will be called when establishing an array of Fred objects?
[Recently Changed SO IT Uses New-Style Headers and the Std :: Syntax and Reworded References To STL (on 7/00). Click Here to Go To The Next Faq in The "Chain" of Recent Changes
]
The default constructor of FRED (except the following discussion).
You can't tell the compiler to call different constructor (except the following discussion). If your Fred class does not have a default constructor, trying to create a Fred object array will result in erroneous compile.
Class Fred {public: Fred (INT I, INT J);
// ... assume that the Fred class does not have the default constructor ...
}; Int main () {fred a [10];
// Error: Fred class does not have a default constructor
Fred * p = new fred [10];
// Error: Fred class does not have a default constructor
}
However, if you are creating a standard std :: Vector
#include
// 10 Fred objects in Std :: Vector will use Fred (5, 7) to initialize
// ...
}
Although the std :: Vector rather than array should be used, there should be an array of groups, such as the "explicit initialization" syntax of the "array. It looks like this:
Class Fred {public: Fred (INT I, INT J);
// ... assume that the Fred class does not have the default constructor ...
}; Int main () {Fred A [10] = {Fred (5, 7), Fred (5, 7), Fred (5, 7), Fred (5, 7), Fred (5, 7), Fred (5, 7), FRED (5, 7), Fred (5, 7), Fred (5, 7), Fred (5, 7)};
// 10 Fred objects will be initialized using FRED (5, 7).
// ...
}
Of course, you don't have to do Fred (5, 7) every item - you can put any numbers you want, or even parameters or other variables. The focus is, this syntax is (a) is feasible, but (b) is not as beautiful as std :: vector syntax. Remember this: Array is harmful - use arrays unless compiled, otherwise it should be replaced with std :: vector. [TOP | BOTTOM | Previous Section | Next Section]
[10.6] Constructor should be "initialized list" or "assignment"?
[Recently ReWrote (on 4/01). Click Here to Go To The Next Faq in The "Chain" of Recent Changes
]
Initialization list. In fact, the constructor should initialize all members objects in the initialization list.
For example, the constructor is initialized by the initialization list Fred: Fred (): X_ (wherever) {} to initialize the member object X_. This is the most common benefit is to improve performance. For example, wherever expressions and member variables X_ are the same, the result of the Whatever expression is directly constructed from internal X_ - the compiler does not generate two copies of the object. Even if the type is different, the compiler can usually be done better than the use assignment.
Another (wrong) method for establishing a constructor is to assign a value, such as Fred :: fred () {x_ = wherever;}. In this case, the Whatever expression causes a separate, temporary object being established, and the temporary object is passed to the assignment of the X_ object. The temporary object is then classified; This is the low efficiency.
This is not too bad, but there is also a source of inefficiencies using assignments in the constructor: member objects will be constructed with the default constructor, for example, may allocate some default amounts of memory or open some missing Provincial documents. But if the WHATEVER expression and / or assignment operation causes object to close the file and / or release that block memory, these work is to do useless work (for example, if the default constructor is not assigned a sufficiently large memory pool or it opens an error document).
Conclusion: When other conditions are equal, the code using the initialization list is faster than the assigned code.
Note: If the type of x_ is a built-in type such as int or char * or FLOAT, it is not distinctive. But even in these cases, my personal preference is to make it well, still use the initial list instead of assignment to set these data members.
[TOP | BOTTOM | Previous Section | Next Section]
[10.7] Can I use the THIS pointer in the constructor?
[Recently REWROTE BECAUSE OF A Suggestion from Perry Rapp (on 4/01). Click Here To Go To The Next Faq in The "Chain" of Recent Changes
]
Some people think that the THIS pointer should not be used in the constructor, because the THIS object is not fully formed. Then, as long as you are careful, you can use this in the constructor (in the function body even in the initial list).
The following is always possible: a function of constructor (or a function called by constructor) reliably accesses data members declared in the base class and / or the data member belonging to the category of console. This is because all of these data members are guaranteed to be completely established when constructed functional enforcement begins.
The following is an endless: constructor's function body (or a function called by the constructor) cannot be called down the deficient function. If your purpose is to get a function of derived class rewritten, then you will reach it. Note, no matter how you call virtual member functions: Explicitly use this pointer (such as this-> method ()), implicitly use this pointer (eg Method ()), or even call other functions on the THIS object Calling the virtual member function, you will not get a derived rewrite function. This is the bottom line: Even if the caller is building an object of a derived class, the object is not a derived object during the execution of the constructor of the base class. The following is sometimes feasible: if you pass any of the data members of the THIS object to another data member, you must ensure that the data is already initialized. The good news is that you can use a significant language rule that does not depend on the compiler you use, to determine if the data member has been (or yet) is initialized. Bad news is that you must know these language rules (for example, the base class sub-object is first initialized (if there is multiple and / or virtual inheritance, query this order!), Then the data defined in the class is based on the order declared in the class. It is initialized). If you don't know these rules, don't pass any data members from this object (whether it is explicitly using this keyword) to give any other data member's initialization program! If you know these rules, you need to be careful.
[TOP | BOTTOM | Previous Section | Next Section]
[10.8] What is "Named Constructor Idiom"?
[Recently Fixed a TYPO (Fred vs. Point) in The ProSe Thanks To Roy Lecates (on 7/00). Click Here to Go To The Next Faq in The "Chain" of Recent Changes
]
A more intuitive and / or safer construction skills provided for users of your class.
The problem is that the constructor always has the same name as the class. Therefore, the different constructor distinguishes the class is through the parameter list. But if there are many constructor, the differences between them are sometimes sensitive and have erroneous tendencies.
Using Named Constructor IDiom, in Private: Section and Protected: All classes of constructor are declared, and a public static method that returns an object is provided. These methods are here called "Named Constructors". Generally, there is such a static method for each different constructor.
For example, suppose we are building a Point class depicting the X-Y plane. There are usually two ways to specify a two-dimensional spatial coordinate: rectangular coordinate (X Y), polar coordinate (radius angle). (Don't worry, I have forgotten these; the focus is not the episode of the coordinate system; the focus is in that there are several ways to create a Point object.) Unfortunately, the parameters of the two coordinate systems are the same: Two float. This will cause an overloaded error in the overload constructor:
Class Point {public: Point (Float X, float y);
// Rectangular coordinates
Point (float r, float a); // extreme coordinate (radius and angle)
// Error: Overload is not clear: Point :: Point (Float, Float)
}; Int main () {POINT P = Point (5.7, 1.2);
// Not clear: Which coordinate system?
}
One way to solve this uncertain mistake is to use the named constructor IDIOM:
#include
// TO GET SIN () and cos ()
Class Point {public: static point reccTangular (float x, float y);
// Rectangular coordinates
Static Point Polar (Float Radius, Float Angle);
// extreme coordinates
// These static methods are called "Named Construction" "
// ...
PRIVATE: POINT (Float X, Float Y);
// Rectangular coordinates
Float x_, y_;}; inline point :: Point (float x, float y): x_ (x), y_ (y) {} inline point point :: Rectangular (float x, float y) {return Point (x, Y);} inline point point :: Polar (Float Radius, Float Angle) {Return Point (radius * cos (angle), radius * sin (Angle));}
Now, Point's users have a clear and clear syntax to create a Point object in any coordinate system:
INT main () {Point P1 = Point :: Rectangular (5.7, 1.2);
// is obviously a rectangular coordinate
Point P2 = Point :: Polar (5.7, 1.2);
// is obviously a polar coordinate
}
If you expect POINT to be derived, make sure your constructor is in protected: section.
Name constructor usage can also be used to create objects through New.
[TOP | BOTTOM | Previous Section | Next Section]
[10.9] Why not initialize static member data in the initialization list of constructors?
[Recently Added a "," In The Initialization List Thanks to Yaroslav Mironov (ON 4/01). Click Here to Go To The Next Faq in The "Chain" of Recent Changes
]
Because you must explicitly define static data members of the class.
Fred.h:
Class fred {public: fred ();
// ...
PRIVATE: INT i_; static int j_;};
Fred.cpp (or fred.c or other):
Fred :: fred (): i_ (10)
// Correct: Enable (and should) to initialize member data
J_ (42)
// Error: Can not be like this initialization static member data
{
// ...
}
/ / Must define static data members:
INT FRED :: j_ = 42;
[TOP | BOTTOM | Previous Section | Next Section]
[10.10] Why get a link error with a class of static data members?
Because static data members must be explicitly defined in an editing unit. If you don't do this, you may get the "undefined External" link error. For example: // fred.h
Class fred {public:
// ...
PRIVATE: STATIC INT J_;
// Declare static data member: fred :: j_
// ...
}
The linker will complain to you ("Fred :: j_ is not defined") unless you define in a source file (not just a declaration) fred :: j_:
// fred.cpp
#include "fred.h" int Fred :: j_ = Some_Expression_EVALUATING_TO_AN_INT;
// alternative, if you wish to use the implicit 0 value for static INTS:
// int Fred :: j_;
Usually define the static data member of the Fred class is a fred.cpp file (or Fred.c or other extension you use).
[TOP | BOTTOM | Previous Section | Next Section]
[10.11] What is "Static Initialization Order FiaSco"?
The delicate killer of your project.
Static Initialization Order FiaSco is a very subtle and common misunderstanding for C . Unfortunately, it is difficult to detect before the MAIN () starts.
In short, assuming that you have two static objects x and y existing in different source files x.cpp and y.cpp. It is assumed that the constructor of the Y object calls some methods of the X object.
that's it. It's that simple.
The ending is 50% -50% of your ending. If you happen to the editing unit of X.cpp first initialize, this is fine. However, if Y.CPP's editing unit is first initialized, the Y's constructor is then run first than the constructor of the X. That is, the constructor of Y is called the method of the X object, and the X object has not been constructed.
I heard that they were employed by McDonald's. Enjoy the new job of their chopper meat.
If you think that the Russian square in the corner of the bedroom is exciting, you can go here. On the contrary, if you want to prevent disasters by using a system method, you may want to read the next FAQ.
.
Note: Static Initialization Order Fiasco does not work in built-in / inherent types, icons INT or CHAR *. For example, if you create a Static float object, there is no problem with static initialization order. The timing of the static initialization is really crashing only when your Static or global object is constructed.
[TOP | BOTTOM | Previous Section | Next Section]
[10.12] How to prevent "Static Initialization Order FiaSco?"
Using "Construct On First Use" usage means that the static object is wrapped in the function inside the function.
For example, suppose you have two classes, fred and barney. There is a global Fred object called x, and a global barney object called Y Y. Barney's constructor calls the gobowling () method of the X object. X.cpp file defines an X object:
// file x.cpp
#include "fred.hpp" fred x;
Y.CPP file defines y object: // file y.cpp
#include "barney.hpp" barney y;
All of the Barney constructor looks like this:
// file barney.cpp
#include "barney.hpp" barney :: barney () {
// ...
X.GObowling ();
// ...
}
As described above, since they are located in different source files, the chance of the disaster occurred in the X previous file is 50%.
This issue has many solutions, but a very easy solution is to replace the global Fred object x with a global function X () that returns a Fred object reference.
// file x.cpp
#include "fred.hpp" fred & x () {static fred * ANS = new fred (); return * ans;
Since the static partial object is only constructed when the control flow passes through their declarations, the above new fred () statement will only be executed once: x () is called first call. Each subsequent call will return the same FRED object (the one point to the ANS). Then you have to do it to change X ():
// file barney.cpp
#include "barney.hpp" barney :: barney () {
// ...
x (). GOBOWLING ();
// ...
}
Since the global Fred object is constructed when used for the first time, construct ON First Uses is called for the first time.
This method is unfavorable to the FRED object that is not destructed. C FAQ Book has another technique to eliminate this impact (but face the price of "Static DE-Initialization Order Fiasco).
Note: Don't do this for built-in / inherent types, icons INT or CHAR *. For example, if you create a static or global FLOAT object, you don't need to wrap it in the function. The timing of the static initialization is really crashing only when your Static or global object is constructed.
[TOP | BOTTOM | Previous Section | Next Section]
[10.13] How to prevent "Static Initialization Order FiaSco" for static data members?
Use the same skills as described, but this time you use static member functions instead of global functions.
Suppose the class x has a Static Fred object:
// file x.hpp
Class x {public:
// ...
PRIVATE: STATIC FRED X_;
Naturally, the static member is initialized separately:
// file x.cpp
#include "x.hpp" fred x :: x_;
Natural, Fred objects are used in one or more methods of X:
Void x :: someMethod () {x_.gobowling ();
But now the "disaster scene" is if someone is not known to the Fred object before the Fred object is constructed before being constructed. For example, if someone creates a static X object during static initialization and calls its SomeMethod () method, then you are subject to the compiler to construct X :: X_ before or after the SomeMethod () is called. (ANSI / ISO C committee is trying to solve this problem, but many compilers are generally not completed; pay attention to this future update.) No matter what result, turn the x :: x__ static data member to static Member functions are always the easiest and secure:
// file x.hpp
Class x {public:
// ...
PRIVATE: STATIC FRED & X ();
Naturally, the static member is initialized separately:
// file x.cpp
#include "x.hpp" Fred & x :: x () {static fred * ANS = new fred (); return * ANS;
Then, simply turn X_ to x ():
Void x :: someMethod () {x (). gobowling ();
If you are sensitive and concerned with the overhead that calls x :: someMethod () each time you call, you can set a Static Fred & to replace it. As you remember, the static partial object is only initialized (when the control process crosses their statements), therefore, only x :: x () once: x :: someMethod () is first called:
Void x :: SomeMethod () {static fred & x = x :: x (); x.gobowling ();
Note: Don't do this for built-in / inherent types, icons INT or CHAR *. For example, if you create a static or global FLOAT object, you don't need to wrap it in the function. The timing of the static initialization is really crashing only when your Static or global object is constructed.
[TOP | BOTTOM | Previous Section | Next Section]
[10.14] How to deal with the failure of constructor?
Throw an exception. See [17.2] for details.
[TOP | BOTTOM | Previous Section | Next Section]
[10.15] What is "Named Parameter Idiom"?
[Recently Created (on 4/01). Click Here to Go To The Next FAQ in The "Chain" of Recent Changes
]
A very useful way to explore the method chain.
The most basic problem solved by Named Parameter Idiom is that C supports location-related parameters. For example, a function caller cannot say "This value is given XYZ, and another value is given to PQR". It can only be said in C (and C and Java) "This is the first parameter, this is the second parameter, etc.". The named parameters of the ADA language are implemented, especially for functions with a large amount of default parameters.
Over the years, people have constructed many programs to make up for the lack of naming parameters of C and C . These include hidden the parameter value to a string parameter and then resolve this string at runtime. For example, this is the approach of fopen () of the second parameter. Another approach is to combine all Boolean parameters into a bitmap, and then the caller generates the constant of this pile of converted to generate an actual parameter. For example, this is the approach of the second parameter of Open (). These methods can work, but the following technology generated by the caller's code is more obvious, easier to write, easier to read, and generally more elegant. This idea is called the Named Parameter IDiom. It is a method that changes the parameters of the function in a new way to the categories created in a new way. These methods returns * THIS by reference. Then you will rename the main function to the parametric "casual" method in that class.
An example will explain the above paragraph.
This example implements the concept of "opening a file". This conceptually requires a parameter of a file name, and some parameters that allow selection, whether the file is opened or written or written; if the file does not exist; it is created; it is written from the end (Add " Append ") or written from the beginning (override" overwrite "); if the file is created, specify the block size; if I / O has a buffer, the buffer size; the file is shared or exclusive access; and other possible options . If we implement this concept with a function of the parameters related parameters, then the caller's code will be very difficult to read: 8 optional parameters, and the caller is likely to make mistakes. So we replace them with named parameters.
Before implementing it, if you want to accept all the default parameters of the function, look at what the caller's code is like:
File f = OpenFile ("foo.txt");
That is a simple situation. Now look at it if you want to change a lot of parameters:
File f = OpenFile ("foo.txt"). Readonly (). CreateIfnotexist (). Appendwhenwriting (). Blocksize (1024). UnBuffered (). ExclusiveAccess ();
Note these "parameters", which are called fair in random order (location-independent) and have names. Therefore, the programmer does not have to remember the order of the parameters, and these names are obvious (just as desired).
The following is how to implement: First create a new class (OpenFile), which contains all parameter values as private: data members. All methods (readonly (), blocksize (unsigned), etc.) returns * this (that is, a reference to an OpenFile object is returned to allow the method to be called by chain). Finally, a constructor of the OpenFile associated with the necessary parameters (herein, the file name) is completed.
Class file; class openfile {public: OpenFile (const string & filename);
/ / Set the default value for each data member
Openfile & readonly ();
// turn Readonly_ to True
OpenFile & Createifnotexist (); OpenFile & Blocksize (unsigned nbytes); // ...
PRIVATE: Friend File; Bool ReadOnly_;
// Default is false [Example]
// ...
Unsigned blocksize_;
// Default is 4096 [Example]
// ...
}
Another thing to do is to make the File constructor with an OpenFile object:
Class File {public: file (const openfile & params);
// vacuums the actual params out of the openfile object
// ...
}
Note that OpenFile declares File as a friend, so OpenFile does not require a lot of piles (and useless) public: get method.
[TOP | BOTTOM | Previous Section | Next Section]
E-mail the author [C FAQ Lite | Table of Contents | Subject Index | About The Author | © | Download Your Own Copy] Revised Apr 8, 2001