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

📄 index.html

📁 C程序员手册(英文)
💻 HTML
📖 第 1 页 / 共 4 页
字号:
<tt>{</tt>
<tt>private:</tt>
<tt>  int secs;</tt>
<tt>  int minutes;</tt>
<tt>  int hours;</tt>
<tt>public:</tt>
<tt>  //...</tt>
<tt>  DateTime::DateTime(ifstream&amp; archive); //reconstituting constructor</tt>
<tt>  ofstream&amp; Write(ofstream&amp; archive);</tt>
<tt>  ifstream&amp; Read(ifstream&amp;  archive);</tt>
<tt>};</tt>
<tt>ofstream&amp;  DateTime::Write(ofstream&amp; archive)</tt>
<tt>{</tt>
<tt>  Date::Write(archive); //must invoke base class Write() first</tt>
<tt>  archive.write( reinterpret_cast&lt;char*&gt; (&amp;), sizeof(day));</tt>
<tt>  archive.write( reinterpret_cast&lt;char*&gt; (&amp;month), sizeof(month));</tt>
<tt>  archive.write( reinterpret_cast&lt;char*&gt; (&amp;month), sizeof(year));</tt>
<tt>  return archive;</tt>
<tt>}</tt>
<tt>ifstream&amp;  DateTime::Read(ifstream&amp; archive)</tt>
<tt>{</tt>
<tt>  Date::Read(archive);</tt>
<tt>  archive.read( reinterpret_cast&lt;char*&gt; (&amp;day), sizeof(day));</tt>
<tt>  archive.read( reinterpret_cast&lt;char*&gt; (&amp;month), sizeof(month));</tt>
<tt>  archive.read( reinterpret_cast&lt;char*&gt; (&amp;month), sizeof(year));</tt>
<tt>  return archive;</tt>
<tt>}</tt>
<tt>DateTime::DateTime(ifstream&amp; archive) //reconstituting constructor</tt>
<tt>{</tt>
<tt>  Read(arcive);</tt>
<tt>}</tt>
</pre>
<h4> Third Party Classes</h4>
<p>Overriding the member functions <tt>Read()</tt> and <tt>Write()</tt> and serializing 
  data members to and from a stream are error prone and can cause maintenance 
  difficulties. Whenever data members are added or removed, or when their types 
  are changed, the implementer has to modify these member functions accordingly 
  -- but this is still managable. However, deriving from classes that do not define 
  a reconstituting constructor and the member functions <tt>Read()</tt> and <tt>Write()</tt> 
  is more difficult to handle because a derived class can only serialize its own 
  members -- not members of its base classes. The same difficulties exist with 
  embedded objects. How are such subobjects serialized? It might be possible to 
  overcome these difficulties in some cases, albeit with considerable efforts. 
  For example, a class that contains a <tt>vector</tt> can iterate through the 
  vector's members and serialize them one by one. This is only half the story, 
  though. A vector's state depends on other parameters, such as its capacity. 
  Where can this information be stored if the <tt>vector</tt> object itself cannot 
  be serialized? Serializing arrays is another conundrum. One solution is to write 
  a header in the beginning of every serialized object that contains the number 
  of elements. However, this won't work with reference counted objects. Most implementations 
  of <tt>std::string</tt> are reference counted, which means that in the following 
  code snippet, the five <tt>string</tt> objects share some of their data members:</p>
<pre>
<tt>#include &lt;string&gt;</tt>
<tt>using namespace std;</tt>
<tt>void single_string()</tt>
<tt>{</tt>
<tt>  string sarr[4];</tt>
<tt>  string s = sarr[0];</tt>
<tt>  for (int i = 1; i&lt; 4; i++)</tt>
<tt>  {</tt>
<tt>    sarr[i] = s;</tt>
<tt>  }</tt>
<tt>}</tt>
</pre>
<p>Reference counting is an implementation detail that is hidden from the users 
  of the class; it is impossible to query the <tt>string</tt> object about how 
  many strings it represents and to serialize this datum.</p>
<p>Handmade object persistence is becoming much more complicated than it seemed 
  at first, isn't it? But that's not all yet. How might such a handmade persistence 
  model represent templates? By simply storing specializations as ordinary objects, 
  the model fails to represent the relationship that exists among the specializations. 
  Worse yet, multiple inheritance and virtual inheritance are even more challenging. 
  How can a handmade persistence model ensure that a virtual subobject is serialized 
  only once, regardless of the number of its occurrences in the inheritance graph? 
</p>
<h4> A Proposal for a Standardized Persistence Model</h4>
<p>Most programmers probably give in at this point, and rightfully so. It is possible 
  to come up with a solution even to the virtual base class problem, but as soon 
  as this problem is solved, other special cases such as function objects, static 
  data members, reference variables, and unions present more complexities. There 
  is another drawback in the handmade persistence model: It is not standardized, 
  and as such, programmers have to implement it on their own. The result is a 
  lack of uniformity and varying levels of reliability and performance. Without 
  standardized support for object persistence, a homemade persistence model is, 
  at best, brittle and error prone. Obviously, without standardized object persistence 
  it is impossible to ensure simple, portable, and efficient serialization and 
  deserialization of objects. </p>
<h4> Library-Based Extensions</h4>
<p>What might such a standardized persistence model look like? There are two basic 
  strategies. One is library-based, whereas the other relies on core language 
  extensions (keywords and syntax). A library-based solution is advantageous in 
  many respects. For example, it does not extend the core language, thus avoiding 
  additional burden for programmers who do not intend to use persistent objects. 
  In addition, a library can be replaced by a better implementation from another 
  vendor without having to switch to a different compiler. This practice can be 
  seen today with people who uninstall the original STL implementation -- provided 
  by the compiler vendor -- and replace it with another one. Still, a library-based 
  solution has to deal with the lack of language support for persistence, and 
  it must face the same difficulties and complications that were demonstrated 
  previously (the intricacies and vagaries of the most widely used object distribution 
  frameworks, namely the Distributed Component Object Model (DCOM) and the Common 
  Object Request Broker Architecture (CORBA), prove this point). STL might have 
  never become what it is today without built-in support for templates and operator 
  overloading. Furthermore, the language support for templates was extended in 
  various ways to provide the necessary constructs for STL (see Chapter 2, "Standard 
  Briefing: The Latest Addenda to ANSI/ISO C++,"<i> </i>and Chapter 9). Similarly, 
  the support for persistence requires core language extensions.</p>
<h4> A New Constructor Type</h4>
<p>The special member functions are automatically synthesized by the implementation 
  if the programmer does not declare them explicitly and if the implementation 
  needs them (see Chapter 4). Similarly, a language extension can be made so that 
  another type of constructor, a <i>reconstituting constructor</i>, is either 
  implicitly synthesized by the implementation when needed, or so that it can 
  be declared by the programmer. As is the case with other constructor types, 
  the programmers need to be allowed to override the default reconstituting constructor 
  by defining it explicitly. The syntactic form of such a constructor must be 
  distinct from all other constructor forms. In particular, a reconstituting constructor 
  is not to be identified solely by its signature. In other words, the following</p>
<pre>
<tt>class A</tt>
<tt>{</tt>
<tt>//...</tt>
<tt>public:</tt>
<tt>  A(istream&amp; repository ); //reconstituting ctor or an ordinary constructor</tt>
<tt>};</tt>
</pre>
<p>is not recommended. It might well be the case that the programmer's intention 
  was to define an ordinary constructor that takes an <tt>istream</tt> object 
  by reference and not a reconstituting constructor. Furthermore, such a convention 
  might break existing code. A better approach is to add a syntactic clue that 
  signifies a reconstituting constructor exclusively. For example, by preceding 
  the symbol <tt>&gt;&lt;</tt> to the constructor's name</p>
<pre>
<tt>class A</tt>
<tt>{</tt>
<tt>//...</tt>
<tt>public:</tt>
<tt>  &gt;&lt;A(istream&amp; repository ); //reconstituting constructor</tt>
<tt>};</tt>
</pre>
<p>the reconstituting constructor can take a single parameter of some stream type. 
  This parameter is optional. When the reconstituting constructor is invoked without 
  an argument, the implementation deserializes the object from a default input 
  stream that can be specified in the compiler's setting (similar to the default 
  location of the standard header files). To automate the serialization process, 
  a serializing destructor is also necessary. How might such a destructor be declared? 
  One solution is to add another type of destructor so that classes can have two 
  destructor types. This is, however, troublesome because the object model of 
  C++ is based on a single destructor per class. Adding another type of destructor 
  is ruled out then. Perhaps there is no need to define a distinct destructor 
  type. Instead, the existing destructor can do the serialization automatically: 
  The compiler can insert into the destructor additional code that performs the 
  necessary serialization operations. (As you know, compilers already insert code 
  into user-defined destructors to invoke the destructors of base classes and 
  embedded objects.) </p>
<p>Automating the serialization process has drawbacks, too. Not every class has 
  to be serialized. The overhead of serializing an object should be imposed only 
  when the user really needs it. Furthermore, the possibility of encountering 
  runtime exceptions during serialization is rather high. A full hard disk, a 
  broken network connection, and a corrupted repository are only a handful of 
  the possible runtime exceptions that can occur during the process of writing 
  the contents of an object to a permanent storage medium. However, throwing an 
  exception from a destructor is highly undesirable (see Chapter 6), so perhaps 
  automatic serialization during object destruction is too risky. Apparently, 
  there is no escape from explicitly calling a member function to do the job. 
  There are other obstacles here: How to handle the creation and serialization 
  of an array of objects? How to synchronize changes in the definition of a class 
  and the contents of an object that was serialized before the change took place? 
  Every language that supports object persistence deals with these difficulties 
  in its own way. C++ can borrow some of these ideas, too, or it can initiate 
  innovative ideas.</p>
<p>This discussion gives you some feel of why language extensions are necessary, 
  and what kind of obstacles they overcome. However hypothetical this discussion 
  might seem, the evolution of C++ has been a democratic process. Many of the 
  changes and extensions were initiated by users of the language rather than Standardization 
  committee members. STL is probably the best example of this. If you have a comprehensive 
  proposal for such an extension, you can present it to the Standardization committee.</p>
<h3> <a name="Heading12">Support for Concurrency</a></h3>
<p><i>Concurrency</i> is a generic term for multithreading and multiprocessing. 
  Concurrent programming can effectively improve performance and responsiveness 
  of an application, be it a word processor or a satellite homing system. C++ 
  does not directly address the issues of multiprocessing, threads, and thread 
  safety. It is important to note, however, that nothing in the Standard Library 
  or the language itself <i>disallows </i>concurrency. Look at the example of 
  exception handling: In a multithreaded environment, exception handling should 
  be thread-safe, but a single-threaded environment can implement exception handling 
  in a non-thread-safe manner; this is an implementation-dependent issue. Implementations 
  are allowed to provide the necessary facilities for concurrency, and indeed 
  many of them do so. Again, without direct support from the programming language, 
  either by standardized libraries or by core extensions, the implementation of 
  thread safety is more complicated and highly nonportable. There have been several 
  proposals in the past for adding concurrency to C and C++. At present, however, 
  none of these languages supports concurrency directly. </p>
<h4> Multithreading</h4>
<p><i>Multithreading</i>, as opposed to multiprocessing, refers to the use of 
  several control threads in a single process. Multithreading is therefore simpler 
  to design and implement, and it enables the application to use system resources 
  more effectively.</p>
<p>Because all threads in a process share the process's data, it is essential 
  to synchronize their operation properly so that one thread does not interfere 
  with another. For that purpose, <i>synchronization objects</i> are used. Various 
  types of synchronization objects, such as mutex, critical section, lock, and 
  semaphore offer different levels of resource allocation and protection. Unfortunately, 
  the details and the characterizations of synchronization objects vary from platform 
  to platform. A standard library of synchronization objects has to be flexible 
  enough to enable users to combine platform-specific synchronization objects 
  with standard objects. This is similar to the use of <tt>std::string</tt> and 
  nonstandard string objects in the same program. Alternatively, the standard 
  thread library could provide the basic interfaces of the synchronization objects, 
  and the implementation would be platform-dependent. There is a problem with 
  introducing multithreading support into the Standard, however: single-threaded 
  operating systems such as DOS. Although these platforms are not very popular 
  these days, they are still in use, and implementing a thread library on these 
  platforms is nearly impossible.</p>
<h4> Thread safety</h4>
<p>Perhaps the Standard can provide only the necessary features for thread safety 
  and leave the other issues -- such as synchronization objects, event objects, 
  instantiation, destruction of threads, and so on -- implementation-defined, 
  as they are today. Thread safety ensures that an object can be used safely in 
  a multithreaded environment. For example, the following thread-unsafe class</p>
<pre>
<tt>class Date</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  int day;</tt>
<tt>  int month;</tt>
<tt>  int year;</tt>
<tt>public:</tt>
<tt>  Date(); //current date</tt>
<tt>  ~Date();</tt>
<tt>     //accessors</tt>
<tt>   int getDay() const { return day; }</tt>
<tt>   int getMonth() const { return month; }</tt>
<tt>   int getYear() const { return year; }</tt>
<tt>    //mutators</tt>
<tt>   void setDay(int d)  { day = d; }</tt>
<tt>   void setMonth(int m)  { month = m; }</tt>
<tt>   void setYear(int y)  { year = y; }</tt>
<tt>};</tt>
</pre>
<p>can become thread-safe by applying the following changes to it: At the beginning 
  of each member function, a lock has to be acquired; in every return point of 
  each member function, the lock has to be released.</p>
<p>The modified member functions now look like this:</p>
<pre>
<tt>void Date::setDay(int d)</tt>
<tt>{</tt>

⌨️ 快捷键说明

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