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

📄 index.html

📁 C程序员手册(英文)
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<p>A copy constructor is said to be <i>trivial</i> if it is implicitly declared, 
  if its class has no virtual member functions and no virtual base classes, and 
  if its entire direct base classes and embedded objects have trivial copy constructors. 
  The implementation implicitly defines an implicitly-declared, nontrivial copy 
  constructor to initialize an object of its type from a copy of an object of 
  its type (or from one derived from it). The implicitly-defined copy constructor 
  performs a memberwise copy of its subobjects, as in the following example:</p>
<pre>
<tt>#include&lt;string&gt;</tt>
<tt>using std::string;</tt>
<tt>class Website //no user-defined copy constructor</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  string URL;</tt>
<tt>  unsigned int IP;</tt>
<tt>public:</tt>
<tt>  Website() : IP(0), URL("""") {}</tt>
<tt>};</tt>
<tt>int main ()</tt>
<tt>{</tt>
<tt>  Website site1;</tt>
<tt>  Website site2(site1); //invoke implicitly-defined copy constructor</tt>
<tt>}</tt>
</pre>
<p>The programmer did not declare a copy constructor for class <tt>Website</tt>. 
  Because <tt>Website</tt> has an embedded object of type <tt>std::string</tt>, 
  which happens to have a user-defined copy constructor, the implementation implicitly 
  defines a copy constructor for class <tt>Website</tt> and uses it to copy construct 
  the object <tt>site2</tt> from <tt>site1</tt>. The synthesized copy constructor 
  first invokes the copy constructor of <tt>std::string</tt>, and then performs 
  a bitwise copying of the data members of <tt>site1</tt> into <tt>site2</tt>.</p>
<p>Novices are sometimes encouraged to define the four special member functions 
  for every class they write. As can be seen in the case of the <tt>Website</tt> 
  class, not only is this unnecessary, but it is even undesirable under some conditions. 
  The synthesized copy constructor (and the assignment operator, as you are about 
  to see) already do the "right thing". They automatically invoke the constructors 
  of base and member subobjects, they initialize the virtual pointer (if one exists), 
  and they perform a bitwise copying of fundamental types. In many cases, this 
  is exactly the programmer's intention anyway. Furthermore, the synthesized constructor 
  and copy constructor enable the implementation to create code that is more efficient 
  than user-written code because it can apply optimizations that are not always 
  possible otherwise.</p>
<h3> <a name="Heading16">Implementation-Required Initializations</a></h3>
<p>Like ordinary constructors, copy constructors -- either implicitly-defined 
  or user-defined -- are augmented by the compiler, which inserts additional code 
  into them to invoke the copy constructors of direct base classes and embedded 
  objects. It is guaranteed, however, that virtual base subobjects are copied 
  only once.</p>
<h2> <a name="Heading17">Simulating Virtual Constructors</a></h2>
<p>Unlike ordinary member functions, a constructor has to know the exact type 
  of its object at compile time in order to construct it properly. Consequently, 
  a constructor cannot be declared <tt>virtual</tt>. Still, creating an object 
  without knowing its exact type is useful in certain conditions. The easiest 
  way to simulate virtual construction is by defining a virtual member function 
  that returns a constructed object of its class type. For example</p>
<pre>
<tt>class Browser</tt>
<tt>{</tt>
<tt>public:</tt>
<tt>  Browser();</tt>
<tt>  Browser( const Browser&amp;);</tt>
<tt>  virtual Browser* construct()  </tt>
<tt>    { return new Browser; } //virtual default constructor</tt>
<tt>  virtual Browser* clone() </tt>
<tt>    { return new Browser(*this); } //virtual copy constructor</tt>
<tt>  virtual ~Browser();</tt>
<tt>//...</tt>
<tt>};</tt>
<tt>class HTMLEditor: public Browser</tt>
<tt>{</tt>
<tt>public:</tt>
<tt>  HTMLEditor ();</tt>
<tt>  HTMLEditor (const HTMLEditor &amp;);</tt>
<tt>  HTMLEditor * construct() </tt>
<tt>    { return new HTMLEditor; }//virtual default constructor</tt>
<tt>  HTMLEditor * clone() </tt>
<tt>    { return new HTMLEditor (*this); } //virtual copy constructor</tt>
<tt>  virtual ~HTMLEditor();</tt>
<tt>  //...</tt>
<tt>};</tt>
</pre>
<p>The polymorphic behavior of the member functions <tt>clone()</tt> and <tt>construct()</tt> 
  enables -you to instantiate a new object of the right type, without having to 
  know the exact type of the source object.</p>
<pre>
<tt>void create (Browser&amp; br)</tt>
<tt>{</tt>
<tt>  br.view();</tt>
<tt>  Browser* pbr  = br.construct();</tt>
<tt>  //...use pbr and br</tt>
<tt>  delete pbr;</tt>
<tt>}</tt>
</pre>
<p><tt>pbr</tt> is assigned a pointer to an object of the right type -- either 
  <tt>Browser</tt> or any class publicly derived from it. Note that the object 
  <tt>br</tt> does not delete the new object it has created; this is the user's 
  responsibility. If it did, the lifetime of the reproduced objects would depend 
  on the lifetime of their originator -- which would significantly compromise 
  the usability of this technique.</p>
<h3> <a name="Heading18">Covariance of Virtual Member Functions</a></h3>
<p>The implementation of virtual constructors relies on a recent modification 
  to C++, namely virtual functions' covariance. An overriding virtual function 
  has to match the signature and the return type of the function it overrides. 
  This restriction was recently relaxed to enable the return type of an overriding 
  virtual function to co-vary with its class type. Thus, the return type of a 
  public base can be changed to the type of a derived class. The covariance applies 
  only to pointers and references.</p>
<blockquote>
  <hr>
  <strong>CAUTION: </strong> Please note that some compilers do not support virtual 
  member functions' covariance yet. 
  <hr>
</blockquote>
<h2> <a name="Heading19">Assignment Operator</a></h2>
<p>A user-declared assignment operator of class <tt>C</tt> is a nonstatic, nontemplate 
  member function of its class, taking exactly one argument of type <tt>C</tt>, 
  <tt>C&amp;</tt>, <tt>const C&amp;</tt>, <tt>volatile C&amp;</tt>, or <tt>const 
  volatile C&amp;</tt>.</p>
<h3> <a name="Heading20">Implicitly-Defined Assignment Operator</a></h3>
<p>If there is no user-defined assignment operator for a class, the implementation 
  implicitly declares one. An implicitly-declared assignment operator is an <tt>inline 
  public</tt> member of its class, and it has the form</p>
<pre>
<tt>C&amp; C::operator=(const C&amp;);</tt>
</pre>
<p>if each base class of <tt>C</tt> has an assignment operator whose first argument 
  is a reference to a <tt>const</tt> object of base class type, and if all the 
  nonstatic embedded objects in <tt>C</tt> also have an assignment operator that 
  takes a reference to a <tt>const</tt> object of their type. Otherwise, the implicitly-declared 
  assignment operator is of the following type:</p>
<pre>
<tt>C&amp; C::operator=(C&amp;);</tt>
</pre>
<p>An implicitly-declared assignment operator has an exception specification. 
  The exception specification contains all the exceptions that might be thrown 
  by other special functions that the assignment operator invokes directly. An 
  assignment operator is said to be <i>trivial</i> if it is implicitly declared, 
  if its class has no virtual member functions or virtual base classes, and if 
  its direct base classes and embedded objects have a trivial assignment operator.</p>
<h3> <a name="Heading21">Simulating Inheritance Of Assignment Operator</a></h3>
<p>Because an assignment operator is implicitly declared for a class if it is 
  not declared by the programmer, the assignment operator of a base class is always 
  hidden by the assignment operator of a derived class. In order to extend -- 
  rather than override -- the assignment operator in a derived class, you must 
  first invoke the assignment operator of the base explicitly, and then add the 
  operations that are required for the derived class. For example</p>
<pre>
<tt>class B</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  char *p;</tt>
<tt>public:</tt>
<tt>  enum {size = 10};</tt>
<tt>  const char * Getp() const {return p;}</tt>
<tt>  B() : p ( new char [size] ) {}</tt>
<tt>  B&amp; operator = (const C&amp; other);</tt>
<tt>  {</tt>
<tt>    if (this != &amp;other)</tt>
<tt>      strcpy(p, other.Getp() );</tt>
<tt>    return *this;</tt>
<tt>  }</tt>
<tt> };</tt>
<tt>class D : public B</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  char *q;</tt>
<tt>public:</tt>
<tt>  const char * Getq() const {return q;}</tt>
<tt>  D(): q  ( new char [size] ) {}</tt>
<tt>  D&amp; operator = (const D&amp; other)</tt>
<tt>  {</tt>
<tt>    if (this != &amp;other)</tt>
<tt>    {    </tt>
<tt>     B::operator=(other);  //first invoke base's assignment operator explicitly</tt>
<tt>     strcpy(q, (other.Getq()));  //add extensions here</tt>
<tt>    }</tt>
<tt>    return *this;</tt>
<tt>  }</tt>
<tt>};</tt>
</pre>
<h2> <a name="Heading22"> When Are User-Written Copy Constructors And Assignment 
  Operators Needed?</a></h2>
<p>The synthesized copy constructor and assignment operator perform a memberwise 
  copy. This is the desirable behavior for most uses. However, it can be disastrous 
  for classes that contain pointers, references, or handles. In such cases, you 
  have to define a copy constructor and assignment operator to avoid <i>aliasing</i>. 
  Aliasing occurs when the same resource is used simultaneously by more than one 
  object. For example</p>
<pre>
<tt>#include &lt;cstdio&gt;</tt>
<tt>using namespace std;</tt>
<tt>class Document</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  FILE *pdb;</tt>
<tt>public:</tt>
<tt>  Document(const char *filename) {pdb = fopen(filename, "t");}</tt>
<tt>  Document(FILE *f =NULL) : pdb{}</tt>
<tt>  ~Document() {fclose(pdb);} //bad, no copy constructor </tt>
<tt>                             //or assignment operator defined</tt>
<tt>};</tt>
<tt>void assign(Document&amp; d)</tt>
<tt>{</tt>
<tt>  Document temp("letter.doc");</tt>
<tt>  d = temp;  //Aliasing; both d and temp are pointing to the same file</tt>
<tt>}//temp's destructor is now called and closes file while d is still using it</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt>  Document doc;</tt>
<tt>  assign(doc);</tt>
<tt>  return 0;</tt>
<tt>  //doc now uses a file which has just been closed. disastrous</tt>
<tt>}}//OOPS! doc's destructor is now invoked and closes 'letter.doc' once again</tt>
</pre>
<p>Because the implementer of class <tt>Document</tt> did not define a copy constructor 
  and assignment operator, the compiler defined them implicitly. However, the 
  synthesized copy constructor and assignment operator result in aliasing. An 
  attempt to open or close the same file twice yields undefined behavior. One 
  way to solve this problem is to define an appropriate copy constructor and assignment 
  operator. Please note, however, that the aliasing results from the reliance 
  on low-level language constructs (file pointers in this case), whereas an embedded 
  <tt>fstream</tt> object can perform the necessary checks automatically. In that 
  case, a user-written copy constructor and assignment operator are unnecessary. 
  The same problem occurs when bare pointers to <tt>char</tt> are used as data 
  members instead of as <tt>string</tt> objects. If you use a pointer to <tt>char</tt> 
  rather than <tt>std::string</tt> in class <tt>Website</tt>, you face an aliasing 
  problem as well.</p>
<h2> <a name="Heading23">Implementing Copy Constructor And Assignment Operator</a></h2>
<p>Another conclusion that can be drawn from the preceding example is that whenever 
  you define a copy constructor, you must also define the assignment operator. 
  When you define only one of the two, the compiler creates the missing one -- 
  but it might not work as expected.</p>
<blockquote> 
  <hr>
  <b>The "Big Three Rule" or the "Big Two Rule"? </b> 
  <p> The famous "Big Three Rule" says that if a class needs any of the Big Three 
    member functions (copy constructor, assignment operator, and destructor), 
    it needs them all. Generally, this rule refers to classes that allocate memory 
    from the free store. However, many other classes require only that the Big 
    Two (copy constructor and assignment operator) be defined by the user; the 
    destructor, nonetheless, is not always required. Examine the followingexample:</p>
  <pre>
<tt>class Year</tt>
<tt>{ </tt>
<tt>private: </tt>
<tt>  int y;</tt>
<tt>  bool cached; //has the object been cached? </tt>
<tt>public:</tt>
<tt>  //...</tt>
<tt>  Year(int y);</tt>
<tt>  Year(const Year&amp; other) //cached should not be copied</tt>
<tt>  { </tt>
<tt>    y = other.getYear();</tt>
<tt>  }   </tt>
<tt>  Year&amp; operator =(const Year&amp;other) //cached should not be copied</tt>
<tt>  { </tt>
<tt>    y = other.getYear(); </tt>
<tt>    return *this;</tt>
<tt>  }  </tt>
<tt>  int getYear() const { return y; }</tt>
<tt>};//no destructor required for class Year</tt>
</pre>
  Class <tt>Year</tt> does not allocate memory from the free store, nor does it 
  acquire any other resources during its construction. A destructor is therefore 
  unnecessary. However, the class needs a user-defined copy constructor and assignment 
  operator to ensure that the value of the member that is <tt>cached</tt> is not 
  copied because it is calculated for every individual object separately. 
  <hr>
</blockquote>
<p>When a user-defined copy constructor and assignment operator are needed, it 
  is important to implement them in a way that prevents self-assignment or aliasing. 
  Usually, it is sufficient to fully implement only one of the two, and then define 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -