📄 index.html
字号:
<tt>}</tt>
</pre>
<p>Extensive amounts of legacy C++ code rely on the implicit conversion of constructors.
The C++ Standardization committee was aware of that. In order to not make existing
code break, the implicit conversion was retained. However, a new keyword, <tt>explicit</tt>,
was introduced to the languageto enable the programmer to block the implicit
conversion when it is undesirable. As a rule, a constructor that can be invoked
with a single argument needs to be declared <tt>explicit</tt>. When the implicit
type conversion is intentional and well behaved, the constructor can be used
as an implicit conversion operator.</p>
<h3> <a name="Heading11">Blocking Undesirable Object Instantiation</a></h3>
<p>Sometimes it might be useful to disable programmers from instantiating an object
of a certain class, for example, a class that is meant to be used only as a
base class for others. A <tt>protected</tt> constructor blocks creation of class
instances, yet it does so without disallowing derived objects' instantiation:</p>
<pre>
<tt>class CommonRoot</tt>
<tt>{</tt>
<tt>protected:</tt>
<tt> CommonRoot(){}//no objects of this class should be instantiated</tt>
<tt> virtual ~CommonRoot ();</tt>
<tt>};</tt>
<tt>class Derived: public CommonRoot</tt>
<tt>{</tt>
<tt>public:</tt>
<tt> Derived();</tt>
<tt>};</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> Derived d; // OK, constructor of d has access to </tt>
<tt> //any protected member in its base class</tt>
<tt> CommonRoot cr; //compilation error: attempt to </tt>
<tt> //access a protected member of CommonRoot</tt>
<tt>}</tt>
</pre>
<p>The same effect of blocking instantiation of a class can be achieved by declaring
pure virtual functions. However, these add runtime and space overhead. When
pure virtual functions aren't needed, you can use a <tt>protected</tt> constructor
instead.</p>
<h3> <a name="Heading12">Using Member Initialization Lists</a></h3>
<p>A constructor might have a member initialization (mem-initialization for short)
list that initializes the data members of the class. For example</p>
<pre>
<tt>class Cellphone //1: mem-init</tt>
<tt>{</tt>
<tt>private:</tt>
<tt> long number;</tt>
<tt> bool on;</tt>
<tt>public:</tt>
<tt> Cellphone (long n, bool ison) : number(n), on(ison) {}</tt>
<tt>};</tt>
</pre>
<p>The constructor of <tt>Cellphone</tt> can also be written as follows:</p>
<pre>
<tt>Cellphone (long n, bool ison) //2 initialization within constructor's body</tt>
<tt>{</tt>
<tt> number = n;</tt>
<tt> on = ison;</tt>
<tt>}</tt>
</pre>
<p>There is no substantial difference between the two forms in the case of <tt>Cellphone</tt>'s
constructor. This is due to the way mem-initialization lists are processed by
the compiler. The compiler scans the mem-initialization list and inserts the
initialization code into the constructor's body before any user-written code.
Thus, the constructor in the first example is expanded by the compiler into
the constructor in the second example. Nonetheless, the choice between using
a mem-initialization list and initialization inside the constructor's body is
significant in the following four cases: </p>
<ul>
<li>
<p> Initialization of <tt>const</tt> members</p>
</li>
<p></p>
<li>
<p> Initialization of reference members</p>
</li>
<p></p>
<li>
<p> Passing arguments to a constructor of a base class or an embedded object</p>
</li>
<p></p>
<li>
<p> Initialization of member objects</p>
</li>
</ul>
<p></p>
<p>In the first three cases, a mem-initialization list is mandatory; in the fourth
case, it is optional. Consider the concrete examples that are discussed in the
following paragraphs.'</p>
<h4> const Data Members</h4>
<p><tt>const</tt> data members of a class, including <tt>const</tt> members of
a base or embedded subobject, must be initialized in a mem-initialization list.</p>
<pre>
<tt>class Allocator</tt>
<tt>{</tt>
<tt>private:</tt>
<tt> const int chunk_size;</tt>
<tt>public:</tt>
<tt> Allocator(int size) : chunk_size(size) {}</tt>
<tt>};</tt>
</pre>
<h4> Reference Data Members</h4>
<p>A reference data member must be initialized by a mem-initialization list.</p>
<pre>
<tt>class Phone;</tt>
<tt>class Modem</tt>
<tt>{</tt>
<tt>private:</tt>
<tt> Phone & line;</tt>
<tt>public:</tt>
<tt> Modem(Phone & ln) : line(ln) {}</tt>
<tt>};</tt>
</pre>
<h4> Invoking A Constructor Of A Base Or A Member Object With Arguments</h4>
<p>When a constructor has to pass arguments to the constructor of its base class
or to the constructor of an embedded object, a mem-initializer must be used.</p>
<pre>
<tt>class base</tt>
<tt>{</tt>
<tt>private:</tt>
<tt> int num1;</tt>
<tt> char * text;</tt>
<tt>public:</tt>
<tt> base(int n1, char * t) {num1 = n1; text = t; } //no default constructor</tt>
<tt>};</tt>
<tt>class derived : public base</tt>
<tt>{</tt>
<tt>private:</tt>
<tt> char *buf;</tt>
<tt>public:</tt>
<tt> derived (int n, char * t) : base(n, t) //pass arguments to base constructor </tt>
<tt> { buf = (new char[100]);} </tt>
<tt>};</tt>
</pre>
<h4> Embedded Objects</h4>
<p>Consider the following example:</p>
<pre>
<tt>#include<string></tt>
<tt>using std::string;</tt>
<tt>class Website</tt>
<tt>{</tt>
<tt>private:</tt>
<tt> string URL</tt>
<tt> unsigned int IP</tt>
<tt>public:</tt>
<tt> Website()</tt>
<tt> {</tt>
<tt> URL = "";</tt>
<tt> IP = 0;</tt>
<tt> }</tt>
<tt>};</tt>
</pre>
<p>Class <tt>Website</tt> has an embedded object of type <tt>std::string</tt>.
The syntax rules of the language do not force the usage of mem-initialization
to initialize this member. However, the performance gain in choosing mem-initialization
over initialization inside the constructor's body is significant. Why? The initialization
inside the constructor's body is very inefficient because it requires the construction
of the member <tt>URL</tt>; a temporary <tt>std::string</tt> object is then
constructed from the value <tt>""</tt>, which is in turn assigned to <tt>URL</tt>.
Finally, the temporary object has to be destroyed. The use of a mem-initialization
list, on the other hand, avoids the creation and destruction of a temporary
object (the performance implications of mem-initialization lists are discussed
in further detail in Chapter 12, "Optimizing Your Code"). </p>
<h4> ""The Order Of A Mem-Initialization List Must Match The Order Of Class Member
Declarations</h4>
<p>Due to the performance difference between the two forms of initializing embedded
objects, some programmers use mem-initialization exclusively -- even for fundamental
types. It is important to note, however, that the order of the initialization
list has to match the order of declarations within the class. This is because
the compiler transforms the list so that it coincides with the order of the
declaration of the class members, regardless of the order specified by the programmer.
For example</p>
<pre>
<tt>class Website</tt>
<tt>{</tt>
<tt>private:</tt>
<tt> string URL; //1</tt>
<tt> unsigned int IP; //2</tt>
<tt>public:</tt>
<tt> Website() : IP(0), URL("") {} // initialized in reverse order</tt>
<tt>};</tt>
</pre>
<p>In the mem-initialization list, the programmer first initializes the member
<tt>IP</tt>, and then <tt>URL</tt>, even though <tt>IP</tt> is declared after
<tt>URL</tt>. The compiler transforms the initialization list to the order of
the member declarations within the class. In this case, the reverse order is
harmless. When there are dependencies in the order of initialization list, however,
this transformation can cause unexpected surprises. For example</p>
<pre>
<tt>class string</tt>
<tt>{</tt>
<tt>private:</tt>
<tt> char *buff;</tt>
<tt> int capacity;</tt>
<tt>public:</tt>
<tt> explicit string(int size) : </tt>
<tt> capacity(size), buff (new char [capacity]) {} undefined behavior</tt>
<tt>};</tt>
</pre>
<p>The mem-initialization list in the constructor of <tt>string</tt> does not
follow the order of declaration of <tt>string</tt>'s members. Consequently,
the compiler transforms the list into</p>
<pre>
<tt>explicit string(int size) : </tt>
<tt>buff (new char [capacity]), capacity(size) {} </tt>
</pre>
<p>The member <tt>capacity</tt> specifies the number of bytes that <tt>new</tt>
has to allocate; but it has not been initialized. The results in this case are
undefined. There are two ways to avert this pitfall: Change the order of member
declarations so that <tt>capacity</tt> is declared before <tt>buff</tt>, or
move the initialization of <tt>buff</tt> into the constructor's body.</p>
<h3> <a name="Heading13">The Exception Specification Of An Implicitly-Declared
Default Constructor </a></h3>
<p>An implicitly-declared default constructor has an <i>exception specification</i>
(exception specifications are discussed in Chapter 6, ""Exception Handling"").
The exception specification of an implicitly-declared default constructor contains
all the exceptions of every other special member function that the constructor
invokes directly. For example</p>
<pre>
<tt>struct A </tt>
<tt>{</tt>
<tt> A(); //can throw any type of exception</tt>
<tt>};</tt>
<tt>struct B </tt>
<tt>{</tt>
<tt> B() throw(); //not allowed to throw any exceptions</tt>
<tt>};</tt>
<tt>struct C : public B</tt>
<tt>{</tt>
<tt> //implicitly-declared C::C() throw;</tt>
<tt>}</tt>
<tt>struct D: public A, public B </tt>
<tt>{</tt>
<tt> //implicitly-declared D::D(); </tt>
<tt>};</tt>
</pre>
<p>The implicitly-declared constructor in class <tt>C</tt> is not allowed to throw
any exceptions because it directly invokes the constructor of class <tt>B</tt>,
which is not allowed to throw any exceptions either. On the other hand, the
implicitly-declared constructor in class <tt>D</tt> is allowed to throw any
type of exception because it directly invokes the constructors of classes <tt>A</tt>
and <tt>B</tt>. Since the constructor of class <tt>A</tt> is allowed to throw
any type of exception, <tt>D</tt>'s implicitly-declared constructor has a matching
exception specification. In other words, <tt>D</tt>'s implicitly-declared constructor
allows all exceptions if any function that it directly invokes allows all exceptions;
it allows no exceptions if every function that it directly invokes allows no
exceptions either. As you will see soon, the same rules apply to the exception
specifications of other implicitly-declared special member functions. </p>
<h2> <a name="Heading14">Copy Constructor</a></h2>
<p>A <i>copy constructor</i> is used to initialize its object with another object.
A constructor of a class <tt>C</tt> is a copy constructor if its first argument
is of type <tt>C&</tt>, <tt>const C&</tt>, <tt>volatile C&</tt>,
or <tt>const volatile C&</tt>, and if there are no additional arguments
or if all other arguments have default values. If there is no user-defined copy
constructor for a class, the implementation implicitly declares one. An implicitly-declared
copy constructor is an <tt>inline public</tt> member of its class, and it has
the form</p>
<pre>
<tt>C::C(const C&);</tt>
</pre>
<p>if each base class of <tt>C</tt> has a copy constructor whose first argument
is a reference to a <tt>const</tt> object of the base class type, and if all
the nonstatic embedded objects in <tt>C</tt> also have a copy constructor that
takes a reference to a <tt>const</tt> object of their type. Otherwise, the implicitly-declared
copy constructor is of the following type:</p>
<pre>
<tt>C::C(C&);</tt>
</pre>
<p>An implicitly-declared copy constructor has an exception specification. The
exception specification contains all the exceptions that might be thrown by
other special functions that the copy constructor invokes directly.</p>
<h3> <a name="Heading15"> Implicitly-Defined Copy Constructors</a></h3>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -