📄 ch04.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"><HTML><HEAD> <META NAME="Author" Content="Steph Mineart"> <META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=iso-8859-1"> <TITLE>ANSI/ISO C++ Professional Programmer's Handbook - 4 - Special Member Functions: Default Constructor, Copy Constructor, Destructor, And Assignment Operator </TITLE> <link rel="stylesheet" TYPE="text/css" href="/includes/stylesheets/ebooks.css"></head><BODY TEXT="#000000" BGCOLOR="#FFFFFF"><CENTER><H1><img src="/publishers/que/series/professional/0789720221/button/que.gif" WIDTH="171" HEIGHT="66" ALIGN="BOTTOM" BORDER="0"><BR>ANSI/ISO C++ Professional Programmer's Handbook</H1></CENTER><CENTER> <P><A HREF="/publishers/que/series/professional/0789720221/index.htm"><img src="/publishers/que/series/professional/0789720221/button/contents.gif" WIDTH="128"HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A> <HR></CENTER><H1 align="center">4</H1><h1 align="center"> Special Member Functions: Default Constructor, Copy Constructor, Destructor, And Assignment Operator</h1><address>by Danny Kale</address><ul> <li><a href="#Heading1"> Introduction</a> <li><a href="#Heading2">Constructors</a> <ul> <li><a href="#Heading3">Calling An Object's Member Function From Its Constructor</a> <li><a href="#Heading4">Trivial Constructors</a> <li><a href="#Heading5">Avoid Reduplicating Identical Pieces Of Constructors' Code</a> <li><a href="#Heading6">Is A Default Constructor Always Necessary?</a> <li><a href="#Heading7">Eligibility For STL Containment</a> <li><a href="#Heading8">When Are Default Constructors Undesirable?</a> <li><a href="#Heading9"> Constructors Of Fundamental Types</a> <li><a href="#Heading10">explicit Constructors</a> <li><a href="#Heading11">Blocking Undesirable Object Instantiation</a> <li><a href="#Heading12">Using Member Initialization Lists</a> <li><a href="#Heading13">The Exception Specification Of An Implicitly-Declared Default Constructor </a> </ul> <li><a href="#Heading14">Copy Constructor</a> <ul> <li><a href="#Heading15"> Implicitly-Defined Copy Constructors</a> <li><a href="#Heading16">Implementation-Required Initializations</a> </ul> <li><a href="#Heading17">Simulating Virtual Constructors</a> <ul> <li><a href="#Heading18">Covariance of Virtual Member Functions</a> </ul> <li><a href="#Heading19">Assignment Operator</a> <ul> <li><a href="#Heading20">Implicitly-Defined Assignment Operator</a> <li><a href="#Heading21">Simulating Inheritance Of Assignment Operator</a> </ul> <li><a href="#Heading22"> When Are User-Written Copy Constructors And Assignment Operators Needed?</a> <li><a href="#Heading23">Implementing Copy Constructor And Assignment Operator</a> <li><a href="#Heading24">Blocking Object Copying</a> <li><a href="#Heading25">Destructors</a> <ul> <li><a href="#Heading26"> Explicit Destructor Invocation</a> <li><a href="#Heading27"> Pseudo Destructors</a> <li><a href="#Heading28"> Pure Virtual Destructors</a> </ul> <li><a href="#Heading29">Constructors And Destructors Should Be Minimal</a> <li><a href="#Heading30">Conclusions</a> </ul><hr size=4><h2><a name="Heading1"> Introduction</a></h2><p>Objects are the fundamental unit of abstraction in object-oriented programming. An object, in the broader sense, is a region of memory storage. Class objects have properties that are determined when the object is created. Conceptually, every class object has four <i>special member functions</i>: default constructor, copy constructor, assignment operator, and destructor. If these members are not explicitly declared by the programmer, the implementation implicitly declares them. This chapter surveys the semantics of the special member functions and their role in class design and implementation. This chapter also examines several techniques and guidelines for effective usage of the special member functions. </p><h2> <a name="Heading2">Constructors</a></h2><p>A constructor is used to initialize an object. A default constructor is one that can be invoked without any arguments. If there is no user-declared constructor for a class, and if the class does not contain <tt>const</tt> or reference data members, the implementation implicitly declares a default constructor for it. </p><p>An implicitly-declared default constructor is an <tt>inline</tt> <tt>public</tt> member of its class; it performs the initialization operations that are needed by the implementation to create an object of this type. Note, however, that these operations do not involve initialization of user-declared data members or allocation of memory from the free store. For example</p><pre><tt>class C</tt><tt>{</tt><tt>private:</tt><tt> int n;</tt><tt> char *p;</tt><tt>public:</tt><tt> virtual ~C() {}</tt><tt>};</tt><tt>void f()</tt><tt>{</tt><tt> C obj; // 1 implicitly-defined constructor is invoked</tt><tt>}</tt></pre><p>The programmer did not declare a constructor in class <tt>C</tt> -- an implicit default constructor was declared and defined by the implementation in order to create an instance of class <tt>C</tt> in the line numbered 1. The synthesized constructor does not initialize the data members <tt>n</tt> and <tt>p</tt>, nor does it allocate memory for the latter. These data members have an indeterminate value after <tt>obj</tt> has been constructed. </p><p>This is because the synthesized default constructor performs only the initialization operations that are required by the implementation -- not the programmer -- to construct an object. In this case, <tt>C</tt> is a polymorphic class. An object of this type contains a pointer to the virtual function table of its class. The virtual pointer is initialized by the implicitly-defined constructor. </p><p>Other implementation-required operations that are performed by implicitly-defined constructors are the invocation of a base class constructor and the invocation of the constructor of embedded objects. The implementation does not declare a constructor for a class if the programmer has defined one. For example</p><pre><tt>class C</tt><tt>{</tt><tt>private:</tt><tt> int n;</tt><tt> char *p;</tt><tt>public:</tt><tt> C() : n(0), p(NULL) {} </tt><tt> virtual ~C() {}</tt><tt>};</tt><tt>void f2()</tt><tt>{</tt><tt> C obj; // 1 user-defined constructor is invoked</tt><tt>}</tt></pre><p>Now the data members of the object <tt>obj</tt> are initialized because the user-defined constructor was invoked to create it. Note, however, that the user-defined constructor only initializes the data members <tt>n</tt> and <tt>p</tt>. Obviously, the virtual pointer must have been initialized as well -- otherwise, the program will be ill-formed. But when did the initialization of the virtual pointer take place? The compiler augments the user-defined constructor with additional code, which is inserted into the constructor's body before any user-written code, and performs the necessary initialization of the virtual pointer.</p><h3> <a name="Heading3">Calling An Object's Member Function From Its Constructor</a></h3><p>Because the virtual pointer is initialized in the constructor before any user-written code, it is safe to call member functions (both virtual and nonvirtual) of an object from its constructor. It is guaranteed that the invoked virtual is the one that is defined in the current object (or of the base class, if it has not been overridden in the current object). However, virtual member functions of objects that are derived from the one whose constructor is executing are not called. For example</p><pre><tt>class A</tt><tt>{</tt><tt>public:</tt><tt> virtual void f() {}</tt><tt> virtual void g() {}</tt><tt>};</tt><tt>class B: public A</tt><tt>{</tt><tt>public:</tt><tt> void f () {} // overriding A::f()</tt><tt> B()</tt><tt> {</tt><tt> f(); // calls B::f()</tt><tt> g(); // g() was not overriden in B, therefore calling A::g()</tt><tt> }</tt><tt>};</tt><tt>class C: public B</tt><tt>{</tt><tt>public:</tt><tt> void f () {} //overriding B::f()</tt><tt> };</tt></pre><p>Please note that if the object's member functions refer to data members of the object, it is the 'programmer's responsibility to initialize these data members first -- most preferably with a <i>member-initialization list</i> (member-initialization lists are discussed next). For example</p><pre><tt>class C</tt><tt>{</tt><tt>private:</tt><tt> int n;</tt><tt> int getn() const { cout<<n<<endl; }</tt><tt>public:</tt><tt> C(int j) : n(j) { getn(); } //Fine: n initialized before getn() is called</tt><tt>};</tt></pre><h3> <a name="Heading4">Trivial Constructors</a></h3><p>As you have observed, compilers synthesize a default constructor for every class or struct, unless a constructor was already defined by the user. However, in certain conditions, such a synthesized constructor is redundant:</p><pre><tt>class Empty {}; //class has no base classes, virtual member functions</tt><tt> //or embedded objects</tt><tt>struct Person</tt><tt>{</tt><tt> int age;</tt><tt> char name[20];</tt><tt> double salary;</tt><tt>};</tt><tt>int main()</tt><tt>{</tt><tt> Empty e;</tt><tt> Person p;</tt><tt> p.age = 30;</tt><tt> return 0;</tt><tt>}</tt></pre><p>An implementation can instantiate <tt>Empty</tt> and <tt>Person</tt> objects without a constructor. In such cases, the explicitly-declared constructor is said to be <i>trivial</i>, which means that the implementation does not need it in order to create an instance of its class. A constructor is considered trivial when all the following hold true:</p><ul> <li> <p> Its class has no virtual member functions and no virtual base classes.</p> </li> <p></p> <li> <p> All the direct base classes of the constructor's class have trivial constructors.</p> </li> <p></p> <li> <p> All the member objects in the constructor's class have trivial constructors.</p> </li></ul><p></p><p>You can see that both <tt>Empty</tt> and <tt>Person</tt> fulfill these conditions; therefore, each of them has a trivial constructor. The compiler suppresses the automatic synthesis of a trivial constructor, thereby producing code that is as efficient in terms of size and speed as that which is produced by a C compiler.</p><h3> <a name="Heading5">Avoid Reduplicating Identical Pieces Of Constructors' Code</a></h3><p>It is very common to define a class that has more than one constructor. For instance, a <tt>string</tt> class can define one constructor that takes <tt>const char *</tt> as an argument, another that takes an argument of type <tt>size_t</tt> to indicate the initial capacity of the string, and a default constructor.</p><pre><tt>class string</tt><tt>{</tt><tt>private:</tt><tt> char * pc;</tt><tt> size_t capacity;</tt><tt> size_t length;</tt><tt> enum { DEFAULT_SIZE = 32};</tt><tt>public:</tt><tt> string(const char * s);</tt><tt> string(size_t initial_capacity );</tt><tt> string();</tt><tt>//...other member functions and overloaded operators</tt><tt>};</tt></pre><p>Each of the three constructors performs individual operations. Nonetheless, some identical tasks -- such as allocating memory from the free store and initializing it, or assigning the value of <tt>capacity</tt> to reflect the size of the allocated storage -- are performed in every constructor. Instead of repeating identical pieces of code in each of the constructors, it is better to move the recurring code into a single nonpublic member function. This function is called by every constructor. The results are shorter compilation time and easier future maintenance:</p><pre><tt>class string</tt><tt>{</tt><tt>private:</tt><tt> char * pc;</tt><tt> size_t capacity;</tt><tt> size_t length;</tt><tt> enum { DEFAULT_SIZE = 32};</tt>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -