⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch04.htm

📁 c++语言操作手册
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<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 &amp; line;</tt><tt>public:</tt><tt>   Modem(Phone &amp; 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&lt;string&gt;</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&amp;</tt>, <tt>const C&amp;</tt>, <tt>volatile C&amp;</tt>,   or <tt>const volatile C&amp;</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&amp;);</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&amp;);</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 + -