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

📄 eiintro.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 3 页
字号:
</PRE>
</UL>

<P><A NAME="dingp23"></A><A NAME="12264"></A>
That brings us to constructors. A <I>default constructor</I> is one that can be called without any arguments. Such a constructor either has no parameters or has a default value for every parameter. You generally need a default constructor if you want to define arrays of <NOBR>objects:<SCRIPT>create_link(23);</SCRIPT>
</NOBR></P>

<UL><A NAME="1739"></A>
<PRE>class A {
public:
  A();                                   // default constructor
};
</PRE><A NAME="12898"></A>
<PRE>A arrayA[10];                            // 10 constructors called

class B {
public:
  B(int x = 0);                          // default constructor
};
<A NAME="12901"></A>
B arrayB[10];                            // 10 constructors called,
                                         // each with an arg of 0
</PRE><A NAME="12905"></A>
<PRE><A NAME="p6"></A>class C {
public:
  C(int x);                              // not a&#32;default constructor
};
</PRE><A NAME="12902"></A>
<PRE>C arrayC[10];                            // error!
</PRE>
</UL>

<P><A NAME="dingp24"></A><A NAME="1742"></A>
You may find that your compilers reject arrays of objects when a class's default constructor has default parameter values. For example, some compilers refuse to accept the definition of <CODE>arrayB</CODE> above, even though it receives the blessing of the C++ standard. This is an example of the kind of discrepancy that can exist between the standard's description of C++ and a particular compiler's implementation of the language. Every compiler I know of has a few of these shortcomings. Until compiler vendors catch up to the standard, be prepared to be flexible, and take solace in the certainty that someday in the not-too-distant future, the C++ described in the standard will be the same as the language accepted by C++ <NOBR>compilers.<SCRIPT>create_link(24);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp25"></A><A NAME="1743"></A>
Incidentally, if you want to create an array of objects for which there is no default constructor, the usual ploy is to define an array of <I>pointers</I> instead. Then you can initialize each pointer separately by using <CODE>new</CODE>:<SCRIPT>create_link(25);</SCRIPT>
</P>

<UL><A NAME="1744"></A>
<PRE>C *ptrArray[10];                          // no constructors called
<A NAME="1745"></A>
ptrArray[0] = new C(22);                  // allocate and construct
                                          // 1 C object
</PRE><A NAME="1746"></A>
<PRE>ptrArray[1] = new C(4);                   // ditto
</PRE><A NAME="1747"></A>
<PRE>...
</PRE>
</UL>

<P><A NAME="dingp26"></A><A NAME="1748"></A>
This suffices almost all the time. When it doesn't, you'll probably have to fall back on the more advanced (and hence more obscure) "placement <CODE>new</CODE>" approach described in <A HREF="../MEC/MI4_FR.HTM#5218" TARGET="_top">Item M4</A>.<SCRIPT>create_link(26);</SCRIPT>
</P>

<P><A NAME="dingp27"></A><A NAME="20725"></A>Back on the terminology front, a <I>copy constructor</I> is used to initialize an object with a different object of the same <NOBR>type:<SCRIPT>create_link(27);</SCRIPT>
</NOBR></P>

<UL><A NAME="20804"></A>
<PRE>class String {
public:
  String();                               // default constructor
  String(const String&amp; rhs);              // copy constructor
  ...
</PRE><A NAME="12329"></A>
<PRE>private:
  char *data;
};
</PRE><A NAME="1751"></A>
<PRE>String s1;                                 // call default constructor
String s2(s1);                             // call copy constructor
String s3 = s2;                            // call copy constructor
</PRE>
</UL>

<P><A NAME="dingp28"></A><A NAME="1752"></A>
Probably the most important use of the copy constructor is to define what it means to pass and return objects by value. As an example, consider the following (inefficient) way of writing a function to concatenate two <CODE>String</CODE> <NOBR>objects:<SCRIPT>create_link(28);</SCRIPT>
</NOBR></P>

<UL><A NAME="1753"></A>
<PRE><A NAME="p7"></A>const String operator+(String s1, String s2)
{
  String temp;
</PRE><A NAME="3365"></A>
<PRE>  delete [] temp.data;
</PRE><A NAME="12334"></A>
<PRE>  temp.data =
    new char[strlen(s1.data) + strlen(s2.data) + 1];
</PRE><A NAME="12335"></A>
<PRE>  strcpy(temp.data, s1.data);
  strcat(temp.data, s2.data);
</PRE><A NAME="1755"></A>
<PRE>  return temp;
}
</PRE><A NAME="1756"></A>
<PRE>String a("Hello");
String b(" world");
String c = a + b;                          // c = String("Hello world")
</PRE>
</UL>

<P><A NAME="dingp29"></A><A NAME="1757"></A>
This <CODE>operator+</CODE> takes two <CODE>String</CODE> objects as parameters and returns one <CODE>String</CODE> object as a result. Both the parameters and the result will be passed by value, so there will be one copy constructor called to initialize <CODE>s1</CODE> with <CODE>a</CODE>, one to initialize <CODE>s2</CODE> with <CODE>b</CODE>, and one to initialize <CODE>c</CODE> with <CODE>temp</CODE>. In fact, there might even be some additional calls to the copy constructor if a compiler decides to generate intermediate temporary objects, which it is allowed to do (see <A HREF="../MEC/MI19_FR.HTM#41177" TARGET="_top">Item M19</A>). The important point here is that pass-by-value <I>means</I> "call the copy <NOBR>constructor."<SCRIPT>create_link(29);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp30"></A><A NAME="1758"></A>
By the way, you wouldn't really implement <CODE>operator+</CODE> for <CODE>String</CODE>s like this. Returning a <CODE>const</CODE> <CODE>String</CODE> object is correct (see Items <A HREF="./EI21_FR.HTM#6003" TARGET="_top">21</A> and <A HREF="./EI23_FR.HTM#6210" TARGET="_top">23</A>), but you would want to pass the two parameters by reference (see <A HREF="./EI22_FR.HTM#6133" TARGET="_top">Item 22</A>).<SCRIPT>create_link(30);</SCRIPT>
</P>

<P><A NAME="dingp31"></A><A NAME="12986"></A>
Actually, you wouldn't write <CODE>operator+</CODE> for <CODE>String</CODE>s at all if you could help it, and you should be able to help it almost all the time. That's because the standard C++ library (see <A HREF="./EI49_FR.HTM#8392" TARGET="_top">Item 49</A>) contains a string type (cunningly named <CODE>string</CODE>), as well as an <CODE>operator+</CODE> for <CODE>string</CODE> objects that does almost exactly what the <CODE>operator+</CODE> above does. In this book, I use both <CODE>String</CODE> and <CODE>string</CODE> objects, but I use them in different ways. (Note that the former name is capitalized, the latter name is not.) If I need just a generic string and I don't care how it's implemented, I use the <CODE>string</CODE> type that is part of the standard C++ library. That's what you should do, too. Often, however, I want to make a point about how C++ behaves, and in those cases, I need to show some implementation code. That's when I use the (nonstandard) <CODE>String</CODE> class. As a programmer, you should use the standard <CODE>string</CODE> type whenever you need a string object; the days of developing your own string class as a C++ rite of passage are behind us. However, you still need to understand the issues that go into the development of classes like <A NAME="p8"></A><CODE>string</CODE>. <CODE>String</CODE> is convenient for that purpose (and for that purpose only). As for raw <CODE>char*</CODE>-based strings, you shouldn't use those antique throw-backs unless you have a <i>very</i> good reason. Well-implemented <CODE>string</CODE> types can now be superior to <CODE>char*</CODE>s in virtually every way &#151; including efficiency (see <A HREF="./EI49_FR.HTM#8392" TARGET="_top">Item 49</A> and Items <A HREF="../MEC/MI29_FR.HTM#6073" TARGET="_top">M29</A>-<A HREF="../MEC/MI30_FR.HTM#6074" TARGET="_top">M30</A>).<SCRIPT>create_link(31);</SCRIPT>
</P>

<P><A NAME="dingp32"></A><A NAME="1759"></A>
The next two terms we need to grapple with are <I>initialization</I> and <I>assignment</I>. An object's initialization occurs when it is given a value for the very first time. For objects of classes or structs with constructors, initialization is <I>always</I> accomplished by calling a constructor. This is quite different from object assignment, which occurs when an object that is already initialized is given a new <NOBR>value:<SCRIPT>create_link(32);</SCRIPT>
</NOBR></P>

<UL><A NAME="1760"></A>
<PRE>string s1;                                // initialization
string s2("Hello");                       // initialization
string s3 = s2;                           // initialization
</PRE><A NAME="1761"></A>
<PRE>s1 = s3;                                  // assignment
</PRE>
</UL>

<P><A NAME="dingp33"></A><A NAME="1762"></A>
From a purely operational point of view, the difference between initialization and assignment is that the former is performed by a constructor while the latter is performed by <CODE>operator=</CODE>. In other words, the two processes correspond to different function <NOBR>calls.<SCRIPT>create_link(33);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp34"></A><A NAME="1763"></A>
The reason for the distinction is that the two kinds of functions must worry about different things. Constructors usually have to check their arguments for validity, whereas most assignment operators can take it for granted that their argument is legitimate (because it has already been constructed). On the other hand, the target of an assignment, unlike an object undergoing construction, may already have resources allocated to it. These resources typically must be released before the new resources can be assigned. Frequently, one of these resources is memory. Before an assignment operator can allocate memory for a new value, it must first deallocate the memory that was allocated for the old <NOBR>value.<SCRIPT>create_link(34);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp35"></A><A NAME="1764"></A>
Here is how a <CODE>String</CODE> constructor and assignment operator could be <NOBR>implemented:<SCRIPT>create_link(35);</SCRIPT>
</NOBR></P>

<UL><A NAME="20806"></A>
<PRE>// a possible String constructor
String::String(const char *value)
{
  if (value) {                            // if value ptr isn't null
    data = new char[strlen(value) + 1];
    strcpy(data,value);
  }
  else {                                  // handle null value ptr<A NAME="1"></A><A HREF="#2"><sup>3</sup></A>
  <!--<A HREF="#20816"><sup>1</sup></A>-->
    data = new char[1];
<A NAME="p9"></a>    *data = '\0';                         // add trailing
null char
  }
}
</PRE><A NAME="1766"></A>
<PRE>// a possible String assignment operator
String&amp; String::operator=(const String&amp; rhs)
{
  if (this == &amp;rhs)
    return *this;                          // see <A HREF="./EI17_FR.HTM#2264" TARGET="_top">Item&nbsp;17</A>

  delete [] data;                          // delete old memory
  data =                                   // allocate new memory
    new char[strlen(rhs.data) + 1];

⌨️ 快捷键说明

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