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

📄 ch04.htm

📁 c++语言操作手册
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<tt>  // the following function is called by every user-defined constructor</tt><tt>  void init( size_t cap = DEFAULT_SIZE);   </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><tt>void string::init( size_t cap)</tt><tt>{</tt><tt>  pc = new char[cap];</tt><tt>  capacity = cap;</tt><tt>}</tt><tt>string::string(const char * s)</tt><tt>{</tt><tt>  size_t size = strlen (s);</tt><tt>  init(size + 1); //make room for null terminating character</tt><tt>  length = size;</tt><tt>  strcpy(pc, s);</tt><tt>}</tt><tt>string::string(size_t initial_capacity )</tt><tt>{</tt><tt>  init(initial_capacity);</tt><tt>  length=0;</tt><tt>}</tt><tt>string::string()</tt><tt>{</tt><tt>  init();</tt><tt>  length = 0;</tt><tt>}</tt></pre><h3> <a name="Heading6">Is A Default Constructor Always Necessary?</a></h3><p>A class might have no default constructor. For example</p><pre><tt>class File</tt><tt>{</tt><tt>private:</tt><tt>  string path;</tt><tt>  int mode;</tt><tt>public:</tt><tt>  File(const string&amp; file_path, int open_mode);</tt><tt>  ~File();</tt><tt>};</tt></pre><p>Class <tt>File</tt> has a user-defined constructor that takes two arguments.   The existence of a user-defined constructor blocks the synthesis of an implicitly-declared   default constructor. Because the programmer did not define a default constructor   either, class <tt>File</tt> does not have a default constructor. A class with   no default constructor limits its users to a narrower set of allowed uses. For   instance, when an array of objects is instantiated, the default constructor   -- and only the default constructor -- of each array member is invoked. Therefore,   you cannot instantiate arrays thereof unless you use a complete initialization   list: </p><pre><tt>File folder1[10]; //error, array requires default constructor</tt><tt>File folder2[2] = { File("f1", 1)}; //error, f2[1] still requires</tt><tt>                                    //a default constructor</tt><tt>File folder3[3] = { File("f1", 1), File("f2",2), File("f3",3) }; //OK, </tt><tt>                                                    //fully initialized array</tt><tt>Similar difficulties arise when you attempt to store objects that have no default constructor in STL containers:#include &lt;vector&gt;</tt><tt>using namespace std;</tt><tt>void f()</tt><tt>{</tt><tt>  vector &lt;File&gt; fv(10); //error, File has no default constructor</tt><tt>  vector &lt;File&gt; v; //OK</tt><tt>  v.push_back(File("db.dat", 1)); //OK</tt><tt>  v.resize(10); //error, File has no default constructor</tt><tt>  v.resize(10, File("f2",2)); //OK</tt><tt>}</tt></pre><p> Was the lack of a default constructor in class <tt>File</tt> intentional?   Maybe. Perhaps the implementer considered an array of <tt>File</tt> objects   undesirable because each object in the array needs to somehow acquire its path   and open mode. However, the lack of a default constructor </p><p>imposes restrictions that are too draconian for most classes.</p><h3> <a name="Heading7">Eligibility For STL Containment</a></h3><p>In order to qualify as an element in an STL container, an object must possess   a copy constructor, an assignment operator, and a destructor as public members   (more on this in Chapter 10, "STL and Generic Programming"). </p><p>A default constructor is also required for certain STL container operations,   as you saw in the preceding example. </p><p>Many operating systems store the files in a directory as a linked list of file   objects. By omitting a default constructor from <tt>File</tt>, the implementer   severely compromises the capability of its users to implement a file system   as a <tt>std::list&lt;File&gt;</tt>.</p><p>For a class such as <tt>File</tt>, whose constructor must initialize its members   with user-supplied values, it might still be possible to define a default constructor.   Instead of supplying the necessary path and open mode as constructor arguments,   a default constructor can read them from a sequential database file.</p><h3> <a name="Heading8">When Are Default Constructors Undesirable?</a></h3><p>Still, a default constructor can be undesirable in some cases. One such case   is a singleton object. Because a singleton object must have one and only one   instance, it is recommended that you block the creation of built-in arrays and   containers of such objects by making the default constructor inaccessible. For   example</p><pre><tt>#include&lt;string&gt;</tt><tt>using namespace std;</tt><tt>int API_getHandle(); //system API function</tt><tt>class Application</tt><tt>{</tt><tt>private:</tt><tt>  string name;</tt><tt>  int handle;</tt><tt>  Application(); // make default constructor inaccessible</tt><tt>public:</tt><tt>  explicit Application(int handle);</tt><tt>  ~Application();</tt><tt>};</tt><tt>int main()</tt><tt>{</tt><tt>  Application theApp( API_getHandle() ); //ok</tt><tt>  Application apps[10]; //error, default constructor is inaccessible</tt><tt>}</tt></pre><p>Class <tt>Application</tt> does not have a default constructor; therefore,   it is impossible to create arrays and containers of <tt>Application</tt> objects.   In this case, the lack of a default constructor is intentional (other implementation   details are still required to ensure that a single instance -- and only a single   instance -- of <tt>Application</tt> is created. However, making the default   constructor inaccessible is one of these details).</p><h3> <a name="Heading9"> Constructors Of Fundamental Types</a></h3><p>Fundamental types such as <tt>char</tt>, <tt>int</tt>, and <tt>float</tt> have   constructors, as do user-defined types. You can initialize a variable by explicitly   invoking its default constructor:</p><pre><tt>int main()</tt><tt>{</tt><tt>  char c = char();</tt><tt>  int n = int ();</tt><tt>  return 0;</tt><tt>}</tt></pre><p>The value that is returned by the explicit invocation of the default constructor   of a fundamental type is equivalent to casting 0 to that type. In other words,</p><pre><tt>char c = char();</tt></pre><p>is equivalent to</p><pre><tt>char c = char(0);</tt></pre><p>Of course, it is possible to initialize a fundamental type with values other   than 0:</p><pre><tt>float f = float (0.333);</tt><tt>char c = char ('a');</tt></pre><p>Normally, you use the shorter notation:</p><pre><tt>char c = 'a';</tt><tt>float f = 0.333;</tt></pre><p>However, this language extension enables uniform treatment in templates for   fundamental types and user-defined types. Fundamental types that are created   on the free store using the operator <tt>new</tt> can be initialized in a similar   manner:</p><pre><tt>int *pi= new int (10); 	</tt><tt>float *pf = new float (0.333);</tt></pre><h3> <a name="Heading10">explicit Constructors</a></h3><p>A constructor that takes a single argument is, by default, an implicit conversion   operator, which converts its argument to an object of its class (see also Chapter   3, "Operator Overloading"). Examine the following concrete example:</p><pre><tt>class string</tt><tt>{</tt><tt>private:</tt><tt>  int size;</tt><tt>  int capacity;</tt><tt>  char *buff;</tt><tt>public:</tt><tt>  string();</tt><tt>  string(int size);  // constructor and implicit conversion operator</tt><tt>  string(const char *); // constructor and implicit conversion operator</tt><tt>  ~string();</tt><tt>};</tt></pre><p>Class <tt>string</tt> has three constructors: a default constructor, a constructor   that takes <tt>int</tt>, and a constructor that constructs a string from <tt>const   char *</tt>. The second constructor is used to create an empty <tt>string</tt>   object with an initial preallocated buffer at the specified size. However, in   the case of class <tt>string</tt>, the automatic conversion is dubious. Converting   an <tt>int</tt> into a string object doesn't make sense, although this is exactly   what this constructor does. Consider the following:</p><pre><tt>int main()</tt><tt>{</tt><tt>  string s = "hello"; //OK, convert a C-string into a string object</tt><tt>  int ns = 0;</tt><tt>  s = 1; // 1 oops, programmer intended to write ns = 1,</tt><tt>}</tt></pre><p>In the expression <tt>s= 1;</tt>, the programmer simply mistyped the name of   the variable <tt>ns,</tt> typing <tt>s</tt> instead. Normally, the compiler   detects the incompatible types and issues an error message. However, before   ruling it out, the compiler first searches for a user-defined conversion that   allows this expression; indeed, it finds the constructor that takes <tt>int</tt>.   Consequently, the compiler interprets the expression <tt>s= 1;</tt> as if the   programmer had written</p><pre><tt>s = string(1);</tt></pre><p>You might encounter a similar problem when calling a function that takes a   <tt>string</tt> argument. The following example can either be a cryptic coding   style or simply a programmer's typographical error. However, due to the implicit   conversion constructor of class <tt>string</tt>, it will pass unnoticed:</p><pre><tt>int f(string s);</tt><tt>int main()</tt><tt>{</tt><tt>  f(1); // without a an explicit constructor,</tt><tt>        //this call is expanded into:  f ( string(1) );</tt><tt>        //was that intentional or merely a programmer's typo?</tt><tt>}</tt></pre><p></p><p>'In order to avoid such implicit conversions, a constructor that takes one   argument needs to be declared <tt>explicit</tt>:</p><pre><tt>class string</tt><tt>{</tt><tt>//...</tt><tt>public:</tt><tt>  explicit string(int size);  // block implicit conversion</tt><tt>  string(const char *); //implicit conversion</tt><tt>  ~string();</tt><tt>};</tt></pre><p>An <tt>explicit</tt> constructor does not behave as an implicit conversion   operator, which enables the compiler to catch the typographical error this time:</p><pre><tt>int main()</tt><tt>{</tt><tt>  string s = "hello"; //OK, convert a C-string into a string object</tt><tt>  int ns = 0;</tt><tt>  s = 1; // compile time error ; this time the compiler catches the typo</tt><tt>}</tt></pre><p>Why aren't all constructors automatically declared <tt>explicit</tt>? Under   some conditions, the automatic type conversion is useful and well behaved. A   good example of this is the third constructor of <tt>string</tt>:</p><pre><tt>string(const char *);</tt></pre><p>The implicit type conversion of <tt>const char *</tt> to a string object enables   its users to write the following:</p><pre><tt>  string s;</tt><tt>  s = "Hello";</tt></pre><p>The compiler implicitly transforms this into</p><pre><tt>  string s;</tt><tt>  //pseudo C++ code:</tt><tt>  s = string ("Hello"); //create a temporary and assign it to s</tt></pre><p>On the other hand, if you declare this constructor <tt>explicit</tt>, you have   to use explicit type conversion:</p><pre><tt>class string</tt><tt>{</tt><tt>//...</tt><tt>public:</tt><tt>  explicit string(const char *);</tt><tt>};</tt><tt>int main()</tt><tt>{</tt><tt>  string s;</tt><tt>  s = string("Hello");  //explicit conversion now required</tt><tt>  return 0;</tt>

⌨️ 快捷键说明

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