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

📄 ei12.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML LANG="EN">
<HEAD>
<title>Effective C++, 2E | Item 12: Prefer initialization to assignment in constructors</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>

<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 0; setCurrentMax(0);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">
var dingbase = "EI12_DIR.HTM";
var dingtext = "Item E12, P";
if (self == top) {
 top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>

</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="E12: Prefer initialization to assignment in ctors." -->
<A NAME="2071"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EI11_FR.HTM" TARGET="_top">Item 11: Declare a copy constructor and an assignment operator for classes with dynamically allocated memory.</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./EI13_FR.HTM" TARGET="_top">Item 13: List members in an initialization list in the order in which they are declared.</A></FONT></DIV>

<P><A NAME="dingp1"></A><FONT ID="eititle">Item 12: &nbsp;Prefer initialization to assignment in constructors.</FONT><SCRIPT>create_link(1);</SCRIPT>
</P><A NAME="2072"></A>
<P><A NAME="dingp2"></A>
Consider a template for generating classes that allow a name to be associated with a pointer to an object of some type <NOBR>T:<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P>
<A NAME="2073"></A>
<UL><PRE><A NAME="p53"></A>template&lt;class T&gt;
class NamedPtr {
public:
  NamedPtr(const string&amp; initName, T *initPtr);
  ...
</PRE>
</UL><A NAME="28441"></A>
<UL><PRE>private:
  string name;
  T *ptr;
};
</PRE>
</UL><A NAME="2075"></A>
<P><A NAME="dingp3"></A>
(In light of the aliasing that can arise during the assignment and copy construction of objects with pointer members (see <A HREF="./EI11_FR.HTM#2042" TARGET="_top">Item 11</A>), you might wish to consider whether <CODE>NamedPtr</CODE> should implement these functions. Hint: it should (see <A HREF="./EI27_FR.HTM#6406" TARGET="_top">Item 27</A>).)<SCRIPT>create_link(3);</SCRIPT>
</P>
<A NAME="2076"></A>
<P><A NAME="dingp4"></A>
When you write the <CODE>NamedPtr</CODE> constructor, you have to transfer the values of the parameters to the corresponding data members. There are two ways to do this. The first is to use the member initialization <NOBR>list:<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P>
<A NAME="15745"></A>
<UL><PRE>template&lt;class T&gt;
NamedPtr&lt;T&gt;::NamedPtr(const string&amp; initName, T *initPtr  )
: name(initName), ptr(initPtr)
{}
</PRE>
</UL><A NAME="2078"></A>
<P><A NAME="dingp5"></A>
The second is to make assignments in the constructor <NOBR>body:<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P>
<A NAME="2079"></A>
<UL><PRE>template&lt;class T&gt;
NamedPtr&lt;T&gt;::NamedPtr(const string&amp; initName, T *initPtr)
{
  name = initName;
  ptr = initPtr;
}
</PRE>
</UL><A NAME="2080"></A>
<P><A NAME="dingp6"></A>
There are important differences between these two <NOBR>approaches.<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P>
<A NAME="2081"></A>
<P><A NAME="dingp7"></A>
From a purely pragmatic point of view, there are times when the initialization list <I>must</I> be used. In particular, <CODE>const</CODE> and reference members may <I>only</I> be initialized, never assigned. So, if you decided that a <CODE>NamedPtr&lt;T&gt;</CODE> object could never change its name or its pointer, you might follow the advice of <A HREF="./EI21_FR.HTM#6003" TARGET="_top">Item 21</A> and declare the members <CODE>const</CODE>:<SCRIPT>create_link(7);</SCRIPT>
</P>
<A NAME="2082"></A>
<UL><PRE>template&lt;class T&gt;
class NamedPtr {
public:
  NamedPtr(const string&amp; initName, T *initPtr);
  ...
</PRE>
</UL><A NAME="28452"></A>
<UL><PRE>private:
  const string name;
  T * const ptr;
};
</PRE>
</UL><A NAME="2084"></A>
<P><A NAME="dingp8"></A>
<A NAME="p54"></A>This class definition <I>requires</I> that you use a member initialization list, because <CODE>const</CODE> members may only be initialized, never <NOBR>assigned.<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>
<A NAME="2085"></A>
<A NAME="dingp9"></A>You'd obtain very different behavior if you decided that a <CODE>NamedPtr&lt;T&gt;</CODE> object should contain a <I>reference</I> to an existing name. Even so, you'd still have to initialize the reference on your constructors' member initialization lists. Of course, you could also combine the two, yielding <CODE>NamedPtr&lt;T&gt;</CODE> objects with read-only access to names that might be modified outside the class:<SCRIPT>create_link(9);</SCRIPT>

<A NAME="2086"></A>
<UL><PRE>template&lt;class T&gt;
class NamedPtr {
public:
  NamedPtr(const string&amp; initName, T *initPtr);
  ...
</PRE>
</UL><A NAME="28459"></A>
<UL><PRE>private:
  const string&amp; name;               // must be initialized via
                                    // initializer list
</PRE>
</UL><A NAME="28457"></A>
<UL><PRE>
  T * const ptr;                    // must be initialized via
                                    // initializer list
};
</PRE>
</UL><A NAME="2088"></A>
<P><A NAME="dingp10"></A>
The original class template, however, contains no <CODE>const</CODE> or reference members. Even so, using a member initialization list is still preferable to performing assignments inside the constructor. This time the reason is efficiency. When a member initialization list is used, only a single <CODE>string</CODE> member function is called. When assignment inside the constructor is used, two are called. To understand why, consider what happens when you declare a <CODE>NamedPtr&lt;T&gt;</CODE> <NOBR>object.<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P>
<A NAME="2089"></A>
<P><A NAME="dingp11"></A>
Construction of objects proceeds in two <NOBR>phases:<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P>
<A NAME="2090"></A><OL TYPE="1"><A NAME="dingp12"></A><LI>Initialization of data members. (See also <A HREF="./EI13_FR.HTM#2117" TARGET="_top">Item 13</A>.)<SCRIPT>create_link(12);</SCRIPT>

<A NAME="2091"></A><A NAME="dingp13"></A><LI>Execution of the body of the constructor that was called.<SCRIPT>create_link(13);</SCRIPT>

</OL>
<A NAME="2092"></A>
<P><A NAME="dingp14"></A>
(For objects with base classes, base class member initialization and constructor body execution occurs prior to that for derived <NOBR>classes.)<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P>
<A NAME="2093"></A>
<P><A NAME="dingp15"></A>
For the <CODE>NamedPtr</CODE> classes, this means that a constructor for the <CODE>string</CODE> object <CODE>name</CODE> will <I>always</I> be called before you ever get inside the body of a <CODE>NamedPtr</CODE> constructor. The only question, then, is this: which <CODE>string</CODE> constructor will be <NOBR>called?<SCRIPT>create_link(15);</SCRIPT>
</NOBR></P>
<A NAME="2094"></A>
<P><A NAME="dingp16"></A>
That depends on the member initialization list in the <CODE>NamedPtr</CODE> classes. If you fail to specify an initialization argument for <CODE>name</CODE>, the default <CODE>string</CODE> constructor will be called. When you later perform an assignment to <CODE>name</CODE> inside the <CODE>NamedPtr</CODE> constructors, you will call <A NAME="p55"></A><CODE>operator=</CODE> on <CODE>name</CODE>. That will total two calls to <CODE>string</CODE> member functions: one for the default constructor and one more for the <NOBR>assignment.<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P>
<A NAME="2095"></A>
<P><A NAME="dingp17"></A>
On the other hand, if you use a member initialization list to specify that <CODE>name</CODE> should be initialized with <CODE>initName</CODE>, <CODE>name</CODE> will be initialized through the copy constructor at a cost of only a single function <NOBR>call.<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P>
<A NAME="15038"></A>
<P><A NAME="dingp18"></A>
Even in the case of the lowly <CODE>string</code> type, the cost of an unnecessary function call may be significant, and as classes become larger and more complex, so do their constructors, and so does the cost of constructing objects. If you establish the habit of using a member initialization list whenever you can, not only do you satisfy a requirement for <CODE>const</CODE> and reference members, you also minimize the chances of initializing data members in an inefficient <NOBR>manner.<SCRIPT>create_link(18);</SCRIPT>
</NOBR></P>
<A NAME="15073"></A>
<P><A NAME="dingp19"></A>
In other words, initialization via a member initialization list is <I>always</I> legal, is <I>never</I> less efficient than assignment inside the body of the constructor, and is often <I>more</I> efficient. Furthermore, it simplifies maintenance of the class (see <A HREF="../MEC/MI32_FR.HTM#5373" TARGET="_top">Item M32</A>), because if a data member's type is later modified to something that <I>requires</I> use of a member initialization list, nothing has to <NOBR>change.<SCRIPT>create_link(19);</SCRIPT>
</NOBR></P>
<A NAME="2097"></A>
<P><A NAME="dingp20"></A>
There is one time, however, when it may make sense to use assignment instead of initialization for the data members in a class. That is when you have a large number of data members of <I>built-in types</I>, and you want them all initialized the same way in each constructor. For example, here's a class that might qualify for this kind of <NOBR>treatment:<SCRIPT>create_link(20);</SCRIPT>
</NOBR></P>
<A NAME="2098"></A>
<UL><PRE>class ManyDataMbrs {
public:
  // default constructor
  ManyDataMbrs();
</PRE>
</UL><A NAME="2100"></A>
<UL><PRE>  // copy constructor
  ManyDataMbrs(const ManyDataMbrs&amp; x);
</PRE>
</UL><A NAME="28465"></A>
<UL><PRE>private:
  int a, b, c, d, e, f, g, h;
  double i, j, k, l, m;
};
</PRE>
</UL><A NAME="15076"></A>
<P><A NAME="dingp21"></A>
Suppose you want to initialize all the ints to 1 and all the doubles to 0, even if the copy constructor is used. Using member initialization lists, you'd have to write <NOBR>this:<SCRIPT>create_link(21);</SCRIPT>
</NOBR></P>
<A NAME="2102"></A>
<UL><PRE><A NAME="p56"></A>ManyDataMbrs::ManyDataMbrs()
: a(1), b(1), c(1), d(1), e(1), f(1), g(1), h(1), i(0),
  j(0), k(0), l(0), m(0)
{ ... }
</PRE>
</UL><A NAME="2103"></A>
<UL><PRE>ManyDataMbrs::ManyDataMbrs(const ManyDataMbrs&amp; x)
: a(1), b(1), c(1), d(1), e(1), f(1), g(1), h(1), i(0),
  j(0), k(0), l(0), m(0)
{ ... }
</PRE>
</UL><A NAME="2104"></A>
<P><A NAME="dingp22"></A>
This is more than just unpleasant drudge work. It is error-prone in the short term and difficult to maintain in the long <NOBR>term.<SCRIPT>create_link(22);</SCRIPT>
</NOBR></P>
<A NAME="2105"></A>
<P><A NAME="dingp23"></A>
However, you can take advantage of the fact that there is no operational difference between initialization and assignment for (non-<CODE>const</CODE>, non-reference) objects of built-in types, so you can safely replace the memberwise initialization lists with a function call to a common initialization <NOBR>routine:<SCRIPT>create_link(23);</SCRIPT>
</NOBR></P>
<A NAME="2106"></A>
<UL><PRE>class ManyDataMbrs {
public:
  // default constructor
  ManyDataMbrs();
</PRE>
</UL><A NAME="2108"></A>
<UL><PRE>  // copy constructor
  ManyDataMbrs(const ManyDataMbrs&amp; x);
</PRE>
</UL><A NAME="28472"></A>
<UL><PRE>private:
  int a, b, c, d, e, f, g, h;
  double i, j, k, l, m;
</PRE>
</UL><A NAME="28470"></A>
<UL><PRE>
  void init();                  // used to initialize data
                                // members
};
</PRE>
</UL><A NAME="2109"></A>
<UL><PRE>void ManyDataMbrs::init()
{
  a = b = c = d = e = f = g = h = 1;
  i = j = k = l = m = 0;
}
</PRE>
</UL><A NAME="2110"></A>
<UL><PRE>ManyDataMbrs::ManyDataMbrs()
{
  init();
</PRE>
</UL><A NAME="2111"></A>
<UL><PRE>  ...
</PRE>
</UL><A NAME="2112"></A>
<UL><PRE>}
</PRE>
</UL><A NAME="2113"></A>
<UL><PRE>ManyDataMbrs::ManyDataMbrs(const ManyDataMbrs&amp; x)
{
  init();
</PRE>
</UL><A NAME="2114"></A>
<UL><PRE>  ...
</PRE>
</UL><A NAME="2115"></A>
<UL><PRE>}
</PRE>
</UL><A NAME="2116"></A>
<P><A NAME="dingp24"></A>
<A NAME="p57"></A>Because the initialization routine is an implementation detail of the class, you are, of course, careful to make it <CODE>private</CODE>, <NOBR>right?<SCRIPT>create_link(24);</SCRIPT>
</NOBR></P>
<A NAME="151208"></A>
<P><A NAME="dingp25"></A>
Note that <CODE>static</CODE> class members should <I>never</I> be initialized in a class's constructor. Static members are initialized only once per program run, so it makes no sense to try to "initialize" them each time an object of the class's type is created. At the very least, doing so would be inefficient: why pay to "initialize" an object multiple times? Besides, initialization of static class members is different enough from initialization of their nonstatic counterparts that an entire Item &#151; <A HREF="./EI47.HTM#8299">Item 47</A> &#151; is devoted to the <NOBR>topic.<SCRIPT>create_link(25);</SCRIPT>
</NOBR></P>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EI11_FR.HTM" TARGET="_top">Item 11: Declare a copy constructor and an assignment operator for classes with dynamically allocated memory.</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./EI13_FR.HTM" TARGET="_top">Item 13: List members in an initialization list in the order in which they are declared.</A></FONT></DIV>

</BODY>
</HTML>

⌨️ 快捷键说明

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