📄 index.html
字号:
<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<string></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&);</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 &);</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& 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&</tt>, <tt>const C&</tt>, <tt>volatile C&</tt>, or <tt>const
volatile C&</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& C::operator=(const C&);</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& C::operator=(C&);</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& operator = (const C& other);</tt>
<tt> {</tt>
<tt> if (this != &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& operator = (const D& other)</tt>
<tt> {</tt>
<tt> if (this != &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 <cstdio></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& 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& other) //cached should not be copied</tt>
<tt> { </tt>
<tt> y = other.getYear();</tt>
<tt> } </tt>
<tt> Year& operator =(const Year&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 + -